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:
Doug Bunting 2016-02-05 15:15:09 -08:00
parent c8a9ecc0c1
commit 73695fc443
6 changed files with 660 additions and 301 deletions

View File

@ -4,10 +4,31 @@
namespace Microsoft.AspNetCore.Antiforgery.Internal
{
/// <summary>
/// Used as a per request state.
/// Used to hold per-request state.
/// </summary>
public class AntiforgeryContext
{
public bool HaveDeserializedCookieToken { 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; }
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -46,10 +47,17 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
CheckSSLConfig(httpContext);
var tokenSet = GetTokensInternal(httpContext);
if (tokenSet.IsNewCookieToken)
var antiforgeryContext = GetTokensInternal(httpContext);
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();
}
else
@ -57,7 +65,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
_logger.ReusedCookieToken();
}
return Serialize(tokenSet);
return tokenSet;
}
/// <inheritdoc />
@ -70,8 +78,8 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
CheckSSLConfig(httpContext);
var tokenSet = GetTokensInternal(httpContext);
return Serialize(tokenSet);
var antiforgeryContext = GetTokensInternal(httpContext);
return Serialize(antiforgeryContext);
}
/// <inheritdoc />
@ -90,6 +98,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
_logger.MissingCookieToken(_options.CookieName);
return false;
}
if (tokens.RequestToken == null)
{
_logger.MissingRequestToken(_options.FormFieldName, _options.HeaderName);
@ -97,8 +106,9 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
}
// Extract cookie & request tokens
var deserializedCookieToken = _tokenSerializer.Deserialize(tokens.CookieToken);
var deserializedRequestToken = _tokenSerializer.Deserialize(tokens.RequestToken);
AntiforgeryToken deserializedCookieToken;
AntiforgeryToken deserializedRequestToken;
DeserializeTokens(httpContext, tokens, out deserializedCookieToken, out deserializedRequestToken);
// Validate
string message;
@ -165,30 +175,17 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
private void ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
{
if (httpContext == null)
{
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));
}
Debug.Assert(!string.IsNullOrEmpty(antiforgeryTokenSet.CookieToken));
Debug.Assert(!string.IsNullOrEmpty(antiforgeryTokenSet.RequestToken));
// Extract cookie & request tokens
var deserializedCookieToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.CookieToken);
var deserializedRequestToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.RequestToken);
AntiforgeryToken deserializedCookieToken;
AntiforgeryToken deserializedRequestToken;
DeserializeTokens(
httpContext,
antiforgeryTokenSet,
out deserializedCookieToken,
out deserializedRequestToken);
// Validate
string message;
@ -212,38 +209,26 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
CheckSSLConfig(httpContext);
var cookieToken = GetCookieTokenDoesNotThrow(httpContext);
cookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
SaveCookieTokenAndHeader(httpContext, cookieToken);
var antiforgeryContext = GetCookieTokens(httpContext);
if (!antiforgeryContext.HaveStoredNewCookieToken && antiforgeryContext.NewCookieToken != null)
{
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 AntiforgeryToken ValidateAndGenerateNewCookieToken(AntiforgeryToken cookieToken)
private void SaveCookieTokenAndHeader(HttpContext httpContext, string 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)
{
// Persist the new cookie if it is not null.
_tokenStore.SaveCookieToken(context, cookieToken);
_tokenStore.SaveCookieToken(httpContext, cookieToken);
}
if (!_options.SuppressXFrameOptionsHeader)
@ -251,7 +236,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Adding X-Frame-Options header to prevent ClickJacking. See
// http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10
// 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
{
return _tokenStore.GetCookieToken(context);
var serializedToken = _tokenStore.GetCookieToken(httpContext);
var token = _tokenSerializer.Deserialize(serializedToken);
return token;
}
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 newCookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
if (newCookieToken != null)
var antiforgeryContext = GetCookieTokens(httpContext);
if (antiforgeryContext.NewRequestToken == null)
{
cookieToken = newCookieToken;
var cookieToken = antiforgeryContext.NewCookieToken ?? antiforgeryContext.CookieToken;
antiforgeryContext.NewRequestToken = _tokenGenerator.GenerateRequestToken(httpContext, cookieToken);
}
var requestToken = _tokenGenerator.GenerateRequestToken(
httpContext,
cookieToken);
return new AntiforgeryTokenSetInternal()
{
// Note : The new cookie would be null if the old cookie is valid.
CookieToken = cookieToken,
RequestToken = requestToken,
IsNewCookieToken = newCookieToken != null
};
return antiforgeryContext;
}
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(
_tokenSerializer.Serialize(tokenSet.RequestToken),
_tokenSerializer.Serialize(tokenSet.CookieToken),
antiforgeryContext.NewRequestTokenString,
antiforgeryContext.NewCookieTokenString,
_options.FormFieldName,
_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;
}
}
}
}

View File

@ -5,7 +5,6 @@ using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
@ -14,39 +13,20 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public class DefaultAntiforgeryTokenStore : IAntiforgeryTokenStore
{
private readonly AntiforgeryOptions _options;
private readonly IAntiforgeryTokenSerializer _tokenSerializer;
public DefaultAntiforgeryTokenStore(
IOptions<AntiforgeryOptions> optionsAccessor,
IAntiforgeryTokenSerializer tokenSerializer)
public DefaultAntiforgeryTokenStore(IOptions<AntiforgeryOptions> optionsAccessor)
{
if (optionsAccessor == null)
{
throw new ArgumentNullException(nameof(optionsAccessor));
}
if (tokenSerializer == null)
{
throw new ArgumentNullException(nameof(tokenSerializer));
}
_options = optionsAccessor.Value;
_tokenSerializer = tokenSerializer;
}
public AntiforgeryToken GetCookieToken(HttpContext httpContext)
public string GetCookieToken(HttpContext httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
var services = httpContext.RequestServices;
var contextAccessor = services.GetRequiredService<IAntiforgeryContextAccessor>();
if (contextAccessor.Value != null)
{
return contextAccessor.Value.CookieToken;
}
Debug.Assert(httpContext != null);
var requestCookie = httpContext.Request.Cookies[_options.CookieName];
if (string.IsNullOrEmpty(requestCookie))
@ -55,15 +35,12 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
return null;
}
return _tokenSerializer.Deserialize(requestCookie);
return requestCookie;
}
public async Task<AntiforgeryTokenSet> GetRequestTokensAsync(HttpContext httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
Debug.Assert(httpContext != null);
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);
}
public void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token)
public void SaveCookieToken(HttpContext httpContext, string token)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
Debug.Assert(httpContext != null);
Debug.Assert(token != null);
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 };
// Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default
@ -115,7 +76,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
options.Secure = true;
}
httpContext.Response.Cookies.Append(_options.CookieName, serializedToken, options);
httpContext.Response.Cookies.Append(_options.CookieName, token, options);
}
}
}

View File

@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
{
public interface IAntiforgeryTokenStore
{
AntiforgeryToken GetCookieToken(HttpContext httpContext);
string GetCookieToken(HttpContext httpContext);
/// <summary>
/// Gets the cookie and request tokens from the request.
@ -17,6 +17,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
/// <returns>The <see cref="AntiforgeryTokenSet"/>.</returns>
Task<AntiforgeryTokenSet> GetRequestTokensAsync(HttpContext httpContext);
void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token);
void SaveCookieToken(HttpContext httpContext, string token);
}
}

View File

@ -125,19 +125,30 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public void GetTokens_ExistingInvalidCookieToken_GeneratesANewCookieTokenAndANewFormToken()
{
// Arrange
var contextAccessor = new DefaultAntiforgeryContextAccessor();
// Generate a new cookie.
var context = CreateMockContext(
new AntiforgeryOptions(),
useOldCookie: false,
isOldCookieValid: false);
isOldCookieValid: false,
contextAccessor: contextAccessor);
var antiforgery = GetAntiforgery(context);
// Act
var tokenset = antiforgery.GetTokens(context.HttpContext);
// Assert
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.RequestToken);
Assert.Equal(context.TestTokenSet.NewCookieTokenString, tokenset.CookieToken);
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]
@ -150,10 +161,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
useOldCookie: false,
isOldCookieValid: false);
// This will cause the cookieToken to be null.
// Exception will cause the cookieToken to be null.
context.TokenSerializer
.Setup(o => o.Deserialize("serialized-old-cookie-token"))
.Setup(o => o.Deserialize(context.TestTokenSet.OldCookieTokenString))
.Throws(new Exception("should be swallowed"));
context.TokenGenerator
.Setup(o => o.IsCookieTokenValid(null))
.Returns(false);
var antiforgery = GetAntiforgery(context);
@ -161,36 +175,88 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
var tokenset = antiforgery.GetTokens(context.HttpContext);
// Assert
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.RequestToken);
Assert.Equal(context.TestTokenSet.NewCookieTokenString, tokenset.CookieToken);
Assert.Equal(context.TestTokenSet.FormTokenString, tokenset.RequestToken);
}
[Fact]
public void GetTokens_ExistingValidCookieToken_GeneratesANewFormToken()
{
// Arrange
var contextAccessor = new DefaultAntiforgeryContextAccessor();
var context = CreateMockContext(
new AntiforgeryOptions(),
useOldCookie: true,
isOldCookieValid: true);
isOldCookieValid: true,
contextAccessor: contextAccessor);
var antiforgery = GetAntiforgery(context);
// Act
var tokenset = antiforgery.GetTokens(context.HttpContext);
// Assert
Assert.Equal("serialized-old-cookie-token", tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.RequestToken);
Assert.Null(tokenset.CookieToken);
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]
public void GetAndStoreTokens_ExistingValidCookieToken_NotOverriden()
{
// Arrange
var contextAccessor = new DefaultAntiforgeryContextAccessor();
var context = CreateMockContext(
new AntiforgeryOptions(),
useOldCookie: true,
isOldCookieValid: true);
isOldCookieValid: true,
contextAccessor: contextAccessor);
var antiforgery = GetAntiforgery(context);
// Act
@ -199,20 +265,31 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Assert
// We shouldn't have saved the cookie because it already existed.
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.Equal("serialized-form-token", tokenSet.RequestToken);
Assert.Null(tokenSet.CookieToken);
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 GetAndStoreTokens_NoExistingCookieToken_Saved()
{
// Arrange
var contextAccessor = new DefaultAntiforgeryContextAccessor();
var context = CreateMockContext(
new AntiforgeryOptions(),
useOldCookie: false,
isOldCookieValid: false);
isOldCookieValid: false,
contextAccessor: contextAccessor);
var antiforgery = GetAntiforgery(context);
// Act
@ -220,17 +297,125 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Assert
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("serialized-form-token", tokenSet.RequestToken);
Assert.Equal(context.TestTokenSet.NewCookieTokenString, tokenSet.CookieToken);
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]
public async Task IsRequestValidAsync_FromStore_Failure()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
var contextAccessor = new DefaultAntiforgeryContextAccessor();
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
string message;
context.TokenGenerator
@ -249,13 +434,21 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Assert
Assert.False(result);
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]
public async Task IsRequestValidAsync_FromStore_Success()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
var contextAccessor = new DefaultAntiforgeryContextAccessor();
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
string message;
context.TokenGenerator
@ -275,13 +468,64 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Assert
Assert.True(result);
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]
public async Task ValidateRequestAsync_FromStore_Failure()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
var contextAccessor = new DefaultAntiforgeryContextAccessor();
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
var message = "my-message";
context.TokenGenerator
@ -299,13 +543,21 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
() => antiforgery.ValidateRequestAsync(context.HttpContext));
Assert.Equal("my-message", exception.Message);
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]
public async Task ValidateRequestAsync_FromStore_Success()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
var contextAccessor = new DefaultAntiforgeryContextAccessor();
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
string message;
context.TokenGenerator
@ -324,6 +576,12 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Assert
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]
@ -429,6 +687,49 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
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]
[InlineData(false, "SAMEORIGIN")]
[InlineData(true, null)]
@ -441,9 +742,14 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
{
SuppressXFrameOptionsHeader = suppressXFrameOptions
};
var contextAccessor = new DefaultAntiforgeryContextAccessor();
// 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);
// Act
@ -452,6 +758,102 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Assert
var xFrameOptions = context.HttpContext.Response.Headers["X-Frame-Options"];
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(
@ -484,13 +886,20 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
return builder.BuildServiceProvider();
}
private HttpContext GetHttpContext()
private HttpContext GetHttpContext(IAntiforgeryContextAccessor contextAccessor)
{
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = GetServices();
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;
}
@ -509,24 +918,27 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
TestTokenSet testTokenSet,
bool saveNewCookie = true)
{
var oldCookieToken = testTokenSet.OldCookieToken;
var formToken = testTokenSet.RequestToken;
var oldCookieToken = testTokenSet.OldCookieTokenString;
var formToken = testTokenSet.FormTokenString;
var mockTokenStore = new Mock<IAntiforgeryTokenStore>(MockBehavior.Strict);
mockTokenStore.Setup(o => o.GetCookieToken(context))
.Returns(oldCookieToken);
mockTokenStore
.Setup(o => o.GetCookieToken(context))
.Returns(oldCookieToken);
mockTokenStore.Setup(o => o.GetRequestTokensAsync(context))
.Returns(() => Task.FromResult(new AntiforgeryTokenSet(
testTokenSet.FormTokenString,
testTokenSet.OldCookieTokenString,
"form",
"header")));
mockTokenStore
.Setup(o => o.GetRequestTokensAsync(context))
.Returns(() => Task.FromResult(new AntiforgeryTokenSet(
formToken,
oldCookieToken,
"form",
"header")));
if (saveNewCookie)
{
var newCookieToken = testTokenSet.NewCookieToken;
mockTokenStore.Setup(o => o.SaveCookieToken(context, newCookieToken))
.Verifiable();
var newCookieToken = testTokenSet.NewCookieTokenString;
mockTokenStore
.Setup(o => o.SaveCookieToken(context, newCookieToken))
.Verifiable();
}
return mockTokenStore;
@ -554,10 +966,11 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
private AntiforgeryMockContext CreateMockContext(
AntiforgeryOptions options,
bool useOldCookie = false,
bool isOldCookieValid = true)
bool isOldCookieValid = true,
IAntiforgeryContextAccessor contextAccessor = null)
{
// Arrange
var httpContext = GetHttpContext();
var httpContext = GetHttpContext(contextAccessor);
var testTokenSet = GetTokenSet();
var mockSerializer = GetTokenSerializer(testTokenSet);

View File

@ -4,11 +4,8 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Primitives;
using Moq;
using Xunit;
@ -17,8 +14,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
{
public class DefaultAntiforgeryTokenStoreTest
{
private static readonly ObjectPool<AntiforgerySerializationContext> _pool =
new DefaultObjectPoolProvider().Create(new AntiforgerySerializationContextPooledObjectPolicy());
private readonly string _cookieName = "cookie-name";
[Fact]
@ -31,9 +26,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
CookieName = _cookieName
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var token = tokenStore.GetCookieToken(httpContext);
@ -42,33 +35,6 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
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]
public void GetCookieToken_CookieIsEmpty_ReturnsNull()
{
@ -79,9 +45,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
CookieName = _cookieName
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var token = tokenStore.GetCookieToken(httpContext);
@ -91,57 +55,24 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
}
[Fact]
public void GetCookieToken_CookieIsInvalid_PropagatesException()
public void GetCookieToken_CookieIsNotEmpty_ReturnsToken()
{
// Arrange
var httpContext = GetHttpContext(_cookieName, "invalid-value");
var expectedException = new AntiforgeryValidationException("some exception");
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
mockSerializer
.Setup(o => o.Deserialize("invalid-value"))
.Throws(expectedException);
var expectedToken = "valid-value";
var httpContext = GetHttpContext(_cookieName, expectedToken);
var options = new AntiforgeryOptions()
{
CookieName = _cookieName
};
var tokenStore = new DefaultAntiforgeryTokenStore(
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);
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var token = tokenStore.GetCookieToken(httpContext);
// Assert
Assert.Same(expectedToken, token);
Assert.Equal(expectedToken, token);
}
[Fact]
@ -157,9 +88,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
FormFieldName = "form-field-name",
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
@ -186,9 +115,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
HeaderName = null,
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider(), _pool));
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
@ -214,9 +141,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
HeaderName = "header-name",
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider(), _pool));
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
@ -244,9 +169,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
HeaderName = "header-name",
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider(), _pool));
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
@ -273,9 +196,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
HeaderName = "header-name",
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider(), _pool));
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
@ -300,9 +221,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
HeaderName = "header-name",
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext);
@ -331,9 +250,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
HeaderName = "header-name",
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>());
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
@ -349,7 +266,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public void SaveCookieToken(bool requireSsl, bool? expectedCookieSecureFlag)
{
// Arrange
var token = new AntiforgeryToken();
var token = "serialized-value";
bool defaultCookieSecureValue = expectedCookieSecureFlag ?? false; // pulled from config; set by ctor
var cookies = new MockResponseCookieCollection();
@ -358,32 +275,19 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
.Setup(o => o.Response.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()
{
CookieName = _cookieName,
RequireSsl = requireSsl
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: mockSerializer.Object);
var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options));
// Act
tokenStore.SaveCookieToken(mockHttpContext.Object, token);
// Assert
Assert.Equal(1, cookies.Count);
Assert.NotNull(contextAccessor.Value.CookieToken);
Assert.NotNull(cookies);
Assert.Equal(_cookieName, cookies.Key);
Assert.Equal("serialized-value", cookies.Value);
@ -391,39 +295,24 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
Assert.Equal(defaultCookieSecureValue, cookies.Options.Secure);
}
private HttpContext GetHttpContext(
string cookieName,
string cookieValue,
IAntiforgeryContextAccessor contextAccessor = null)
private HttpContext GetHttpContext(string cookieName, string cookieValue)
{
var cookies = new RequestCookieCollection(new Dictionary<string, string>
{
{ cookieName, cookieValue },
});
return GetHttpContext(cookies, contextAccessor);
return GetHttpContext(cookies);
}
private HttpContext GetHttpContext(
IRequestCookieCollection cookies,
IAntiforgeryContextAccessor contextAccessor = null)
private HttpContext GetHttpContext(IRequestCookieCollection cookies)
{
var httpContext = new DefaultHttpContext();
httpContext.Request.Cookies = cookies;
contextAccessor = contextAccessor ?? new DefaultAntiforgeryContextAccessor();
httpContext.RequestServices = GetServiceProvider(contextAccessor);
return httpContext;
}
private static IServiceProvider GetServiceProvider(IAntiforgeryContextAccessor contextAccessor)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton(contextAccessor);
return serviceCollection.BuildServiceProvider();
}
private class MockResponseCookieCollection : IResponseCookies
{
public string Key { get; set; }