Use DI for all Antiforgery services
This change makes it possible to replace all of the various IAntiforgery*** extensibility points via DI. changes: - Move functionality out of AntiforgeryWorker into Antiforgery - Move services to DI (instead of constructed by Antiforgery) - Cleanup how application/cookie-name is computed - Merge IAntiforgeryTokenGenerator & IAntiforgeryTokenValidator - Unseal classes - Fix use of options in services - Misc test cleanup
This commit is contained in:
parent
e8ac98ad5d
commit
9eeb1de68f
|
|
@ -2,14 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Antiforgery.Internal;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.WebUtilities;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
|
|
@ -20,27 +15,26 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
/// Provides access to the anti-forgery system, which provides protection against
|
||||
/// Cross-site Request Forgery (XSRF, also called CSRF) attacks.
|
||||
/// </summary>
|
||||
public sealed class Antiforgery
|
||||
public class Antiforgery
|
||||
{
|
||||
private static readonly string _purpose = "Microsoft.AspNet.Antiforgery.AntiforgeryToken.v1";
|
||||
private readonly AntiforgeryWorker _worker;
|
||||
private readonly IHtmlEncoder _htmlEncoder;
|
||||
private readonly AntiforgeryOptions _options;
|
||||
private readonly IAntiforgeryTokenGenerator _tokenGenerator;
|
||||
private readonly IAntiforgeryTokenSerializer _tokenSerializer;
|
||||
private readonly IAntiforgeryTokenStore _tokenStore;
|
||||
|
||||
public Antiforgery(
|
||||
[NotNull] IClaimUidExtractor claimUidExtractor,
|
||||
[NotNull] IDataProtectionProvider dataProtectionProvider,
|
||||
[NotNull] IAntiforgeryAdditionalDataProvider additionalDataProvider,
|
||||
[NotNull] IOptions<AntiforgeryOptions> AntiforgeryOptionsAccessor,
|
||||
[NotNull] IHtmlEncoder htmlEncoder,
|
||||
[NotNull] IOptions<DataProtectionOptions> dataProtectionOptions)
|
||||
IOptions<AntiforgeryOptions> antiforgeryOptionsAccessor,
|
||||
IAntiforgeryTokenGenerator tokenGenerator,
|
||||
IAntiforgeryTokenSerializer tokenSerializer,
|
||||
IAntiforgeryTokenStore tokenStore,
|
||||
IHtmlEncoder htmlEncoder)
|
||||
{
|
||||
var AntiforgeryOptions = AntiforgeryOptionsAccessor.Options;
|
||||
var applicationId = dataProtectionOptions.Options.ApplicationDiscriminator ?? string.Empty;
|
||||
AntiforgeryOptions.CookieName = AntiforgeryOptions.CookieName ?? ComputeCookieName(applicationId);
|
||||
|
||||
var serializer = new AntiforgeryTokenSerializer(dataProtectionProvider.CreateProtector(_purpose));
|
||||
var tokenStore = new AntiforgeryTokenStore(AntiforgeryOptions, serializer);
|
||||
var tokenProvider = new AntiforgeryTokenProvider(AntiforgeryOptions, claimUidExtractor, additionalDataProvider);
|
||||
_worker = new AntiforgeryWorker(serializer, AntiforgeryOptions, tokenStore, tokenProvider, tokenProvider, htmlEncoder);
|
||||
_options = antiforgeryOptionsAccessor.Options;
|
||||
_tokenGenerator = tokenGenerator;
|
||||
_tokenSerializer = tokenSerializer;
|
||||
_tokenStore = tokenStore;
|
||||
_htmlEncoder = htmlEncoder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -56,8 +50,21 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
/// </remarks>
|
||||
public string GetHtml([NotNull] HttpContext context)
|
||||
{
|
||||
var html = _worker.GetFormInputElement(context);
|
||||
return html;
|
||||
CheckSSLConfig(context);
|
||||
|
||||
var cookieToken = GetCookieTokenDoesNotThrow(context);
|
||||
var tokenSet = GetTokens(context, cookieToken);
|
||||
cookieToken = tokenSet.CookieToken;
|
||||
var formToken = tokenSet.FormToken;
|
||||
|
||||
SaveCookieTokenAndHeader(context, cookieToken);
|
||||
|
||||
var inputTag = string.Format(
|
||||
"<input name=\"{0}\" type=\"{1}\" value=\"{2}\" />",
|
||||
_htmlEncoder.HtmlEncode(_options.FormFieldName),
|
||||
_htmlEncoder.HtmlEncode("hidden"),
|
||||
_htmlEncoder.HtmlEncode(_tokenSerializer.Serialize(formToken)));
|
||||
return inputTag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -82,7 +89,14 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
// must persist this value in the form of a response cookie, and the existing cookie value
|
||||
// should be discarded. If this value is null when the method completes, the existing
|
||||
// cookie value was valid and needn't be modified.
|
||||
return _worker.GetTokens(context, oldCookieToken);
|
||||
CheckSSLConfig(context);
|
||||
|
||||
var deserializedcookieToken = DeserializeTokenDoesNotThrow(oldCookieToken);
|
||||
var tokenSet = GetTokens(context, deserializedcookieToken);
|
||||
|
||||
var serializedCookieToken = Serialize(tokenSet.CookieToken);
|
||||
var serializedFormToken = Serialize(tokenSet.FormToken);
|
||||
return new AntiforgeryTokenSet(serializedFormToken, serializedCookieToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -92,7 +106,14 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
/// <param name="context">The HTTP context associated with the current call.</param>
|
||||
public async Task ValidateAsync([NotNull] HttpContext context)
|
||||
{
|
||||
await _worker.ValidateAsync(context);
|
||||
CheckSSLConfig(context);
|
||||
|
||||
// Extract cookie & form tokens
|
||||
var cookieToken = _tokenStore.GetCookieToken(context);
|
||||
var formToken = await _tokenStore.GetFormTokenAsync(context);
|
||||
|
||||
// Validate
|
||||
_tokenGenerator.ValidateTokens(context, cookieToken, formToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -103,7 +124,17 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
/// <param name="formToken">The token that was supplied in the request form body.</param>
|
||||
public void Validate([NotNull] HttpContext context, string cookieToken, string formToken)
|
||||
{
|
||||
_worker.Validate(context, cookieToken, formToken);
|
||||
CheckSSLConfig(context);
|
||||
|
||||
// Extract cookie & form tokens
|
||||
var deserializedCookieToken = DeserializeToken(cookieToken);
|
||||
var deserializedFormToken = DeserializeToken(formToken);
|
||||
|
||||
// Validate
|
||||
_tokenGenerator.ValidateTokens(
|
||||
context,
|
||||
deserializedCookieToken,
|
||||
deserializedFormToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -123,17 +154,117 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
/// <param name="context">The HTTP context associated with the current call.</param>
|
||||
public void SetCookieTokenAndHeader([NotNull] HttpContext context)
|
||||
{
|
||||
_worker.SetCookieTokenAndHeader(context);
|
||||
CheckSSLConfig(context);
|
||||
|
||||
var cookieToken = GetCookieTokenDoesNotThrow(context);
|
||||
cookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
||||
|
||||
SaveCookieTokenAndHeader(context, cookieToken);
|
||||
}
|
||||
|
||||
private string ComputeCookieName(string applicationId)
|
||||
// This method returns null if oldCookieToken is valid.
|
||||
private AntiforgeryToken ValidateAndGenerateNewCookieToken(AntiforgeryToken cookieToken)
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
if (!_tokenGenerator.IsCookieTokenValid(cookieToken))
|
||||
{
|
||||
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(applicationId));
|
||||
var subHash = hash.Take(8).ToArray();
|
||||
return WebEncoders.Base64UrlEncode(subHash);
|
||||
// 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(
|
||||
[NotNull] HttpContext httpContext,
|
||||
AntiforgeryToken cookieToken)
|
||||
{
|
||||
if (cookieToken != null)
|
||||
{
|
||||
// Persist the new cookie if it is not null.
|
||||
_tokenStore.SaveCookieToken(httpContext, cookieToken);
|
||||
}
|
||||
|
||||
if (!_options.SuppressXFrameOptionsHeader)
|
||||
{
|
||||
// Adding X-Frame-Options header to prevent ClickJacking. See
|
||||
// http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10
|
||||
// for more information.
|
||||
httpContext.Response.Headers.Set("X-Frame-Options", "SAMEORIGIN");
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckSSLConfig(HttpContext httpContext)
|
||||
{
|
||||
if (_options.RequireSSL && !httpContext.Request.IsHttps)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.AntiforgeryWorker_RequireSSL);
|
||||
}
|
||||
}
|
||||
|
||||
private AntiforgeryToken DeserializeToken(string serializedToken)
|
||||
{
|
||||
return (!string.IsNullOrEmpty(serializedToken))
|
||||
? _tokenSerializer.Deserialize(serializedToken)
|
||||
: null;
|
||||
}
|
||||
|
||||
private AntiforgeryToken DeserializeTokenDoesNotThrow(string serializedToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return DeserializeToken(serializedToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore failures since we'll just generate a new token
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AntiforgeryToken GetCookieTokenDoesNotThrow(HttpContext httpContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _tokenStore.GetCookieToken(httpContext);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore failures since we'll just generate a new token
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AntiforgeryTokenSetInternal GetTokens(HttpContext httpContext, AntiforgeryToken cookieToken)
|
||||
{
|
||||
var newCookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
||||
if (newCookieToken != null)
|
||||
{
|
||||
cookieToken = newCookieToken;
|
||||
}
|
||||
var formToken = _tokenGenerator.GenerateFormToken(
|
||||
httpContext,
|
||||
cookieToken);
|
||||
|
||||
return new AntiforgeryTokenSetInternal()
|
||||
{
|
||||
// Note : The new cookie would be null if the old cookie is valid.
|
||||
CookieToken = newCookieToken,
|
||||
FormToken = formToken
|
||||
};
|
||||
}
|
||||
|
||||
private string Serialize(AntiforgeryToken token)
|
||||
{
|
||||
return (token != null) ? _tokenSerializer.Serialize(token) : null;
|
||||
}
|
||||
|
||||
private class AntiforgeryTokenSetInternal
|
||||
{
|
||||
public AntiforgeryToken FormToken { get; set; }
|
||||
|
||||
public AntiforgeryToken CookieToken { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,9 +12,6 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
{
|
||||
private const string AntiforgeryTokenFieldName = "__RequestVerificationToken";
|
||||
|
||||
private string _cookieName;
|
||||
private string _formFieldName = AntiforgeryTokenFieldName;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the name of the cookie that is used by the anti-forgery
|
||||
/// system.
|
||||
|
|
@ -23,47 +20,19 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
/// If an explicit name is not provided, the system will automatically
|
||||
/// generate a name.
|
||||
/// </remarks>
|
||||
public string CookieName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cookieName;
|
||||
}
|
||||
|
||||
[param: NotNull]
|
||||
set
|
||||
{
|
||||
_cookieName = value;
|
||||
}
|
||||
}
|
||||
public string CookieName { get; [param: NotNull] set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the name of the anti-forgery token field that is used by the anti-forgery system.
|
||||
/// </summary>
|
||||
public string FormFieldName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _formFieldName;
|
||||
}
|
||||
|
||||
[param: NotNull]
|
||||
set
|
||||
{
|
||||
_formFieldName = value;
|
||||
}
|
||||
}
|
||||
public string FormFieldName { get; [param: NotNull] set; } = AntiforgeryTokenFieldName;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether SSL is required for the anti-forgery system
|
||||
/// to operate. If this setting is 'true' and a non-SSL request
|
||||
/// comes into the system, all anti-forgery APIs will fail.
|
||||
/// </summary>
|
||||
public bool RequireSSL
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public bool RequireSSL { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether to suppress the generation of X-Frame-Options header
|
||||
|
|
@ -71,10 +40,6 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
/// header is generated with the value SAMEORIGIN. If this setting is 'true',
|
||||
/// the X-Frame-Options header will not be generated for the response.
|
||||
/// </summary>
|
||||
public bool SuppressXFrameOptionsHeader
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public bool SuppressXFrameOptionsHeader { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
using Microsoft.AspNet.WebUtilities;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
public class AntiforgeryOptionsSetup : ConfigureOptions<AntiforgeryOptions>
|
||||
{
|
||||
public AntiforgeryOptionsSetup(IOptions<DataProtectionOptions> dataProtectionOptionsAccessor)
|
||||
: base((options) => ConfigureOptions(options, dataProtectionOptionsAccessor.Options))
|
||||
{
|
||||
// We want this to run after any user setups to compute a default name if needed.
|
||||
Order = 10000;
|
||||
}
|
||||
|
||||
public static void ConfigureOptions(AntiforgeryOptions options, DataProtectionOptions dataProtectionOptions)
|
||||
{
|
||||
if (options.CookieName == null)
|
||||
{
|
||||
var applicationId = dataProtectionOptions.ApplicationDiscriminator ?? string.Empty;
|
||||
options.CookieName = ComputeCookieName(applicationId);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ComputeCookieName(string applicationId)
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(applicationId));
|
||||
var subHash = hash.Take(8).ToArray();
|
||||
return WebEncoders.Base64UrlEncode(subHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,21 +5,22 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
public sealed class AntiforgeryTokenProvider : IAntiforgeryTokenValidator, IAntiforgeryTokenGenerator
|
||||
public class AntiforgeryTokenGenerator : IAntiforgeryTokenGenerator
|
||||
{
|
||||
private readonly IClaimUidExtractor _claimUidExtractor;
|
||||
private readonly AntiforgeryOptions _config;
|
||||
private readonly AntiforgeryOptions _options;
|
||||
private readonly IAntiforgeryAdditionalDataProvider _additionalDataProvider;
|
||||
|
||||
internal AntiforgeryTokenProvider(
|
||||
AntiforgeryOptions config,
|
||||
public AntiforgeryTokenGenerator(
|
||||
IOptions<AntiforgeryOptions> optionsAccessor,
|
||||
IClaimUidExtractor claimUidExtractor,
|
||||
IAntiforgeryAdditionalDataProvider additionalDataProvider)
|
||||
{
|
||||
_config = config;
|
||||
_options = optionsAccessor.Options;
|
||||
_claimUidExtractor = claimUidExtractor;
|
||||
_additionalDataProvider = additionalDataProvider;
|
||||
}
|
||||
|
|
@ -33,9 +34,9 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
};
|
||||
}
|
||||
|
||||
public AntiforgeryToken GenerateFormToken(HttpContext httpContext,
|
||||
ClaimsIdentity identity,
|
||||
AntiforgeryToken cookieToken)
|
||||
public AntiforgeryToken GenerateFormToken(
|
||||
HttpContext httpContext,
|
||||
AntiforgeryToken cookieToken)
|
||||
{
|
||||
Debug.Assert(IsCookieTokenValid(cookieToken));
|
||||
|
||||
|
|
@ -46,6 +47,7 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
};
|
||||
|
||||
var isIdentityAuthenticated = false;
|
||||
var identity = httpContext.User?.Identity as ClaimsIdentity;
|
||||
|
||||
// populate Username and ClaimUid
|
||||
if (identity != null && identity.IsAuthenticated)
|
||||
|
|
@ -84,7 +86,6 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
|
||||
public void ValidateTokens(
|
||||
HttpContext httpContext,
|
||||
ClaimsIdentity identity,
|
||||
AntiforgeryToken sessionToken,
|
||||
AntiforgeryToken fieldToken)
|
||||
{
|
||||
|
|
@ -92,19 +93,19 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
if (sessionToken == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatAntiforgeryToken_CookieMissing(_config.CookieName));
|
||||
Resources.FormatAntiforgeryToken_CookieMissing(_options.CookieName));
|
||||
}
|
||||
if (fieldToken == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatAntiforgeryToken_FormFieldMissing(_config.FormFieldName));
|
||||
Resources.FormatAntiforgeryToken_FormFieldMissing(_options.FormFieldName));
|
||||
}
|
||||
|
||||
// Do the tokens have the correct format?
|
||||
if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatAntiforgeryToken_TokensSwapped(_config.CookieName, _config.FormFieldName));
|
||||
Resources.FormatAntiforgeryToken_TokensSwapped(_options.CookieName, _options.FormFieldName));
|
||||
}
|
||||
|
||||
// Are the security tokens embedded in each incoming token identical?
|
||||
|
|
@ -117,6 +118,7 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
var currentUsername = string.Empty;
|
||||
BinaryBlob currentClaimUid = null;
|
||||
|
||||
var identity = httpContext.User?.Identity as ClaimsIdentity;
|
||||
if (identity != null && identity.IsAuthenticated)
|
||||
{
|
||||
currentClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity));
|
||||
|
|
@ -9,14 +9,16 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
public sealed class AntiforgeryTokenSerializer : IAntiforgeryTokenSerializer
|
||||
public class AntiforgeryTokenSerializer : IAntiforgeryTokenSerializer
|
||||
{
|
||||
private static readonly string Purpose = "Microsoft.AspNet.Antiforgery.AntiforgeryToken.v1";
|
||||
|
||||
private readonly IDataProtector _cryptoSystem;
|
||||
private const byte TokenVersion = 0x01;
|
||||
|
||||
public AntiforgeryTokenSerializer([NotNull] IDataProtector cryptoSystem)
|
||||
public AntiforgeryTokenSerializer([NotNull] IDataProtectionProvider provider)
|
||||
{
|
||||
_cryptoSystem = cryptoSystem;
|
||||
_cryptoSystem = provider.CreateProtector(Purpose);
|
||||
}
|
||||
|
||||
public AntiforgeryToken Deserialize(string serializedToken)
|
||||
|
|
|
|||
|
|
@ -6,74 +6,77 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
// Saves anti-XSRF tokens split between HttpRequest.Cookies and HttpRequest.Form
|
||||
public sealed class AntiforgeryTokenStore : IAntiforgeryTokenStore
|
||||
public class AntiforgeryTokenStore : IAntiforgeryTokenStore
|
||||
{
|
||||
private readonly AntiforgeryOptions _config;
|
||||
private readonly IAntiforgeryTokenSerializer _serializer;
|
||||
private readonly AntiforgeryOptions _options;
|
||||
private readonly IAntiforgeryTokenSerializer _tokenSerializer;
|
||||
|
||||
public AntiforgeryTokenStore([NotNull] AntiforgeryOptions config,
|
||||
[NotNull] IAntiforgeryTokenSerializer serializer)
|
||||
public AntiforgeryTokenStore(
|
||||
[NotNull] IOptions<AntiforgeryOptions> optionsAccessor,
|
||||
[NotNull] IAntiforgeryTokenSerializer tokenSerializer)
|
||||
{
|
||||
_config = config;
|
||||
_serializer = serializer;
|
||||
_options = optionsAccessor.Options;
|
||||
_tokenSerializer = tokenSerializer;
|
||||
}
|
||||
|
||||
public AntiforgeryToken GetCookieToken(HttpContext httpContext)
|
||||
{
|
||||
var contextAccessor =
|
||||
httpContext.RequestServices.GetRequiredService<IAntiforgeryContextAccessor>();
|
||||
var services = httpContext.RequestServices;
|
||||
var contextAccessor = services.GetRequiredService<IAntiforgeryContextAccessor>();
|
||||
if (contextAccessor.Value != null)
|
||||
{
|
||||
return contextAccessor.Value.CookieToken;
|
||||
}
|
||||
|
||||
var requestCookie = httpContext.Request.Cookies[_config.CookieName];
|
||||
var requestCookie = httpContext.Request.Cookies[_options.CookieName];
|
||||
if (string.IsNullOrEmpty(requestCookie))
|
||||
{
|
||||
// unable to find the cookie.
|
||||
return null;
|
||||
}
|
||||
|
||||
return _serializer.Deserialize(requestCookie);
|
||||
return _tokenSerializer.Deserialize(requestCookie);
|
||||
}
|
||||
|
||||
public async Task<AntiforgeryToken> GetFormTokenAsync(HttpContext httpContext)
|
||||
{
|
||||
var form = await httpContext.Request.ReadFormAsync();
|
||||
var value = form[_config.FormFieldName];
|
||||
var value = form[_options.FormFieldName];
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
// did not exist
|
||||
return null;
|
||||
}
|
||||
|
||||
return _serializer.Deserialize(value);
|
||||
return _tokenSerializer.Deserialize(value);
|
||||
}
|
||||
|
||||
public void SaveCookieToken(HttpContext httpContext, AntiforgeryToken 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 contextAccessor =
|
||||
httpContext.RequestServices.GetRequiredService<IAntiforgeryContextAccessor>();
|
||||
|
||||
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 = _serializer.Serialize(token);
|
||||
var serializedToken = _tokenSerializer.Serialize(token);
|
||||
var options = new CookieOptions() { HttpOnly = true };
|
||||
|
||||
// Note: don't use "newCookie.Secure = _config.RequireSSL;" since the default
|
||||
// Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default
|
||||
// value of newCookie.Secure is poulated out of band.
|
||||
if (_config.RequireSSL)
|
||||
if (_options.RequireSSL)
|
||||
{
|
||||
options.Secure = true;
|
||||
}
|
||||
|
||||
httpContext.Response.Cookies.Append(_config.CookieName, serializedToken, options);
|
||||
httpContext.Response.Cookies.Append(_options.CookieName, serializedToken, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@ using Microsoft.AspNet.Http;
|
|||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
// Provides configuration information about the anti-forgery system.
|
||||
/// <summary>
|
||||
/// Generates and validates antiforgery tokens.
|
||||
/// </summary>
|
||||
public interface IAntiforgeryTokenGenerator
|
||||
{
|
||||
// Generates a new random cookie token.
|
||||
|
|
@ -16,7 +18,16 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
// The incoming cookie token must be valid.
|
||||
AntiforgeryToken GenerateFormToken(
|
||||
HttpContext httpContext,
|
||||
ClaimsIdentity identity,
|
||||
AntiforgeryToken cookieToken);
|
||||
|
||||
// Determines whether an existing cookie token is valid (well-formed).
|
||||
// If it is not, the caller must call GenerateCookieToken() before calling GenerateFormToken().
|
||||
bool IsCookieTokenValid(AntiforgeryToken cookieToken);
|
||||
|
||||
// Validates a (cookie, form) token pair.
|
||||
void ValidateTokens(
|
||||
HttpContext httpContext,
|
||||
AntiforgeryToken cookieToken,
|
||||
AntiforgeryToken formToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
// Provides an abstraction around something that can validate anti-XSRF tokens
|
||||
public interface IAntiforgeryTokenValidator
|
||||
{
|
||||
// Determines whether an existing cookie token is valid (well-formed).
|
||||
// If it is not, the caller must call GenerateCookieToken() before calling GenerateFormToken().
|
||||
bool IsCookieTokenValid(AntiforgeryToken cookieToken);
|
||||
|
||||
// Validates a (cookie, form) token pair.
|
||||
void ValidateTokens(
|
||||
HttpContext httpContext,
|
||||
ClaimsIdentity identity,
|
||||
AntiforgeryToken cookieToken,
|
||||
AntiforgeryToken formToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,251 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
|
||||
namespace Microsoft.AspNet.Antiforgery.Internal
|
||||
{
|
||||
public sealed class AntiforgeryWorker
|
||||
{
|
||||
private readonly AntiforgeryOptions _config;
|
||||
private readonly IAntiforgeryTokenSerializer _serializer;
|
||||
private readonly IAntiforgeryTokenStore _tokenStore;
|
||||
private readonly IAntiforgeryTokenValidator _validator;
|
||||
private readonly IAntiforgeryTokenGenerator _generator;
|
||||
private readonly IHtmlEncoder _htmlEncoder;
|
||||
|
||||
public AntiforgeryWorker(
|
||||
[NotNull] IAntiforgeryTokenSerializer serializer,
|
||||
[NotNull] AntiforgeryOptions config,
|
||||
[NotNull] IAntiforgeryTokenStore tokenStore,
|
||||
[NotNull] IAntiforgeryTokenGenerator generator,
|
||||
[NotNull] IAntiforgeryTokenValidator validator,
|
||||
[NotNull] IHtmlEncoder htmlEncoder)
|
||||
{
|
||||
_serializer = serializer;
|
||||
_config = config;
|
||||
_tokenStore = tokenStore;
|
||||
_generator = generator;
|
||||
_validator = validator;
|
||||
_htmlEncoder = htmlEncoder;
|
||||
}
|
||||
|
||||
private void CheckSSLConfig(HttpContext httpContext)
|
||||
{
|
||||
if (_config.RequireSSL && !httpContext.Request.IsHttps)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.AntiforgeryWorker_RequireSSL);
|
||||
}
|
||||
}
|
||||
|
||||
private AntiforgeryToken DeserializeToken(string serializedToken)
|
||||
{
|
||||
return (!string.IsNullOrEmpty(serializedToken))
|
||||
? _serializer.Deserialize(serializedToken)
|
||||
: null;
|
||||
}
|
||||
|
||||
private AntiforgeryToken DeserializeTokenDoesNotThrow(string serializedToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return DeserializeToken(serializedToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore failures since we'll just generate a new token
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static ClaimsIdentity ExtractIdentity(HttpContext httpContext)
|
||||
{
|
||||
if (httpContext != null)
|
||||
{
|
||||
var user = httpContext.User;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
// We only support ClaimsIdentity.
|
||||
return user.Identity as ClaimsIdentity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private AntiforgeryToken GetCookieTokenDoesNotThrow(HttpContext httpContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _tokenStore.GetCookieToken(httpContext);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore failures since we'll just generate a new token
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// [ ENTRY POINT ]
|
||||
// Generates an anti-XSRF token pair for the current user. The return
|
||||
// value is the hidden input form element that should be rendered in
|
||||
// the <form>. This method has a side effect: it may set a response
|
||||
// cookie.
|
||||
public string GetFormInputElement([NotNull] HttpContext httpContext)
|
||||
{
|
||||
CheckSSLConfig(httpContext);
|
||||
|
||||
var cookieToken = GetCookieTokenDoesNotThrow(httpContext);
|
||||
var tokenSet = GetTokens(httpContext, cookieToken);
|
||||
cookieToken = tokenSet.CookieToken;
|
||||
var formToken = tokenSet.FormToken;
|
||||
|
||||
SaveCookieTokenAndHeader(httpContext, cookieToken);
|
||||
|
||||
var inputTag = string.Format(
|
||||
"<input name=\"{0}\" type=\"{1}\" value=\"{2}\" />",
|
||||
_htmlEncoder.HtmlEncode(_config.FormFieldName),
|
||||
_htmlEncoder.HtmlEncode("hidden"),
|
||||
_htmlEncoder.HtmlEncode(_serializer.Serialize(formToken)));
|
||||
return inputTag;
|
||||
}
|
||||
|
||||
// [ ENTRY POINT ]
|
||||
// Generates a (cookie, form) serialized token pair for the current user.
|
||||
// The caller may specify an existing cookie value if one exists. If the
|
||||
// 'new cookie value' out param is non-null, the caller *must* persist
|
||||
// the new value to cookie storage since the original value was null or
|
||||
// invalid. This method is side-effect free.
|
||||
public AntiforgeryTokenSet GetTokens([NotNull] HttpContext httpContext, string cookieToken)
|
||||
{
|
||||
CheckSSLConfig(httpContext);
|
||||
var deSerializedcookieToken = DeserializeTokenDoesNotThrow(cookieToken);
|
||||
var tokenSet = GetTokens(httpContext, deSerializedcookieToken);
|
||||
|
||||
var serializedCookieToken = Serialize(tokenSet.CookieToken);
|
||||
var serializedFormToken = Serialize(tokenSet.FormToken);
|
||||
return new AntiforgeryTokenSet(serializedFormToken, serializedCookieToken);
|
||||
}
|
||||
|
||||
private AntiforgeryTokenSetInternal GetTokens(HttpContext httpContext, AntiforgeryToken cookieToken)
|
||||
{
|
||||
var newCookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
||||
if (newCookieToken != null)
|
||||
{
|
||||
cookieToken = newCookieToken;
|
||||
}
|
||||
var formToken = _generator.GenerateFormToken(
|
||||
httpContext,
|
||||
ExtractIdentity(httpContext),
|
||||
cookieToken);
|
||||
|
||||
return new AntiforgeryTokenSetInternal()
|
||||
{
|
||||
// Note : The new cookie would be null if the old cookie is valid.
|
||||
CookieToken = newCookieToken,
|
||||
FormToken = formToken
|
||||
};
|
||||
}
|
||||
|
||||
private string Serialize(AntiforgeryToken token)
|
||||
{
|
||||
return (token != null) ? _serializer.Serialize(token) : null;
|
||||
}
|
||||
|
||||
// [ ENTRY POINT ]
|
||||
// Given an HttpContext, validates that the anti-XSRF tokens contained
|
||||
// in the cookies & form are OK for this request.
|
||||
public async Task ValidateAsync([NotNull] HttpContext httpContext)
|
||||
{
|
||||
CheckSSLConfig(httpContext);
|
||||
|
||||
// Extract cookie & form tokens
|
||||
var cookieToken = _tokenStore.GetCookieToken(httpContext);
|
||||
var formToken = await _tokenStore.GetFormTokenAsync(httpContext);
|
||||
|
||||
// Validate
|
||||
_validator.ValidateTokens(httpContext, ExtractIdentity(httpContext), cookieToken, formToken);
|
||||
}
|
||||
|
||||
// [ ENTRY POINT ]
|
||||
// Given the serialized string representations of a cookie & form token,
|
||||
// validates that the pair is OK for this request.
|
||||
public void Validate([NotNull] HttpContext httpContext, string cookieToken, string formToken)
|
||||
{
|
||||
CheckSSLConfig(httpContext);
|
||||
|
||||
// Extract cookie & form tokens
|
||||
var deserializedCookieToken = DeserializeToken(cookieToken);
|
||||
var deserializedFormToken = DeserializeToken(formToken);
|
||||
|
||||
// Validate
|
||||
_validator.ValidateTokens(
|
||||
httpContext,
|
||||
ExtractIdentity(httpContext),
|
||||
deserializedCookieToken,
|
||||
deserializedFormToken);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generates and sets an anti-forgery cookie if one is not available or not valid. Also sets response headers.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context associated with the current call.</param>
|
||||
public void SetCookieTokenAndHeader([NotNull] HttpContext httpContext)
|
||||
{
|
||||
CheckSSLConfig(httpContext);
|
||||
|
||||
var cookieToken = GetCookieTokenDoesNotThrow(httpContext);
|
||||
cookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
||||
|
||||
SaveCookieTokenAndHeader(httpContext, cookieToken);
|
||||
}
|
||||
|
||||
// This method returns null if oldCookieToken is valid.
|
||||
private AntiforgeryToken ValidateAndGenerateNewCookieToken(AntiforgeryToken cookieToken)
|
||||
{
|
||||
if (!_validator.IsCookieTokenValid(cookieToken))
|
||||
{
|
||||
// Need to make sure we're always operating with a good cookie token.
|
||||
var newCookieToken = _generator.GenerateCookieToken();
|
||||
Debug.Assert(_validator.IsCookieTokenValid(newCookieToken));
|
||||
return newCookieToken;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SaveCookieTokenAndHeader(
|
||||
[NotNull] HttpContext httpContext,
|
||||
AntiforgeryToken cookieToken)
|
||||
{
|
||||
if (cookieToken != null)
|
||||
{
|
||||
// Persist the new cookie if it is not null.
|
||||
_tokenStore.SaveCookieToken(httpContext, cookieToken);
|
||||
}
|
||||
|
||||
if (!_config.SuppressXFrameOptionsHeader)
|
||||
{
|
||||
// Adding X-Frame-Options header to prevent ClickJacking. See
|
||||
// http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10
|
||||
// for more information.
|
||||
httpContext.Response.Headers.Set("X-Frame-Options", "SAMEORIGIN");
|
||||
}
|
||||
}
|
||||
|
||||
private class AntiforgeryTokenSetInternal
|
||||
{
|
||||
public AntiforgeryToken FormToken { get; set; }
|
||||
|
||||
public AntiforgeryToken CookieToken { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Antiforgery;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.Framework.DependencyInjection
|
||||
{
|
||||
|
|
@ -14,11 +15,17 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
services.AddDataProtection();
|
||||
services.AddWebEncoders();
|
||||
|
||||
services.TryAdd(ServiceDescriptor.Singleton<IClaimUidExtractor, DefaultClaimUidExtractor>());
|
||||
services.TryAdd(ServiceDescriptor.Singleton<Antiforgery, Antiforgery>());
|
||||
services.TryAdd(ServiceDescriptor.Scoped<IAntiforgeryContextAccessor, AntiforgeryContextAccessor>());
|
||||
services.TryAdd(
|
||||
ServiceDescriptor.Singleton<IAntiforgeryAdditionalDataProvider, DefaultAntiforgeryAdditionalDataProvider>());
|
||||
// Don't overwrite any options setups that a user may have added.
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IConfigureOptions<AntiforgeryOptions>, AntiforgeryOptionsSetup>());
|
||||
|
||||
services.TryAddSingleton<IAntiforgeryTokenGenerator, AntiforgeryTokenGenerator>();
|
||||
services.TryAddSingleton<IAntiforgeryTokenSerializer, AntiforgeryTokenSerializer>();
|
||||
services.TryAddSingleton<IAntiforgeryTokenStore, AntiforgeryTokenStore>();
|
||||
services.TryAddSingleton<IClaimUidExtractor, DefaultClaimUidExtractor>();
|
||||
services.TryAddSingleton<Antiforgery, Antiforgery>();
|
||||
services.TryAddScoped<IAntiforgeryContextAccessor, AntiforgeryContextAccessor>();
|
||||
services.TryAddSingleton<IAntiforgeryAdditionalDataProvider, DefaultAntiforgeryAdditionalDataProvider>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
public class AntiforgeryOptionsSetupTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("HelloWorldApp", "tGmK82_ckDw")]
|
||||
[InlineData("TodoCalendar", "7mK1hBEBwYs")]
|
||||
public void AntiforgeryOptionsSetup_SetsDefaultCookieName_BasedOnApplicationId(
|
||||
string applicationId,
|
||||
string expectedCookieName)
|
||||
{
|
||||
// Arrange
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddAntiforgery();
|
||||
serviceCollection.ConfigureDataProtection(o => o.SetApplicationName(applicationId));
|
||||
|
||||
var services = serviceCollection.BuildServiceProvider();
|
||||
var options = services.GetRequiredService<IOptions<AntiforgeryOptions>>();
|
||||
|
||||
// Act
|
||||
var cookieName = options.Options.CookieName;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedCookieName, cookieName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AntiforgeryOptionsSetup_UserOptionsSetup_CanSetCookieName()
|
||||
{
|
||||
// Arrange
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddAntiforgery();
|
||||
serviceCollection.ConfigureDataProtection(o => o.SetApplicationName("HelloWorldApp"));
|
||||
|
||||
serviceCollection.Configure<AntiforgeryOptions>(o =>
|
||||
{
|
||||
Assert.Null(o.CookieName);
|
||||
o.CookieName = "antiforgery";
|
||||
}, order: 9999);
|
||||
|
||||
var services = serviceCollection.BuildServiceProvider();
|
||||
var options = services.GetRequiredService<IOptions<AntiforgeryOptions>>();
|
||||
|
||||
// Act
|
||||
var cookieName = options.Options.CookieName;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("antiforgery", cookieName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,160 +1,127 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if DNX451
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Antiforgery.Internal;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.Framework.WebEncoders.Testing;
|
||||
#if DNX451
|
||||
using Moq;
|
||||
#endif
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
public class AntiforgeryWorkerTest
|
||||
public class AntiforgeryTest
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public async Task ChecksSSL_ValidateAsync_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var mockHttpContext = new Mock<HttpContext>();
|
||||
mockHttpContext.Setup(o => o.Request.IsHttps)
|
||||
.Returns(false);
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var worker = new AntiforgeryWorker(
|
||||
config: config,
|
||||
serializer: null,
|
||||
tokenStore: null,
|
||||
generator: null,
|
||||
validator: null,
|
||||
htmlEncoder: new CommonTestEncoder());
|
||||
var antiforgery = GetAntiforgery(options);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
await
|
||||
Assert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await worker.ValidateAsync(mockHttpContext.Object));
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await antiforgery.ValidateAsync(httpContext));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||
"but the current request is not an SSL request.",
|
||||
ex.Message);
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChecksSSL_Validate_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var mockHttpContext = new Mock<HttpContext>();
|
||||
mockHttpContext.Setup(o => o.Request.IsHttps)
|
||||
.Returns(false);
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var worker = new AntiforgeryWorker(
|
||||
config: config,
|
||||
serializer: null,
|
||||
tokenStore: null,
|
||||
generator: null,
|
||||
validator: null,
|
||||
htmlEncoder: new CommonTestEncoder());
|
||||
var antiforgery = GetAntiforgery(options);
|
||||
|
||||
// Act & assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(
|
||||
() => worker.Validate(mockHttpContext.Object, cookieToken: null, formToken: null));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||
"but the current request is not an SSL request.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChecksSSL_GetFormInputElement_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var mockHttpContext = new Mock<HttpContext>();
|
||||
mockHttpContext.Setup(o => o.Request.IsHttps)
|
||||
.Returns(false);
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var worker = new AntiforgeryWorker(
|
||||
config: config,
|
||||
serializer: null,
|
||||
tokenStore: null,
|
||||
generator: null,
|
||||
validator: null,
|
||||
htmlEncoder: new CommonTestEncoder());
|
||||
|
||||
// Act & assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => worker.GetFormInputElement(mockHttpContext.Object));
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => antiforgery.Validate(httpContext, cookieToken: null, formToken: null));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||
"but the current request is not an SSL request.",
|
||||
ex.Message);
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChecksSSL_GetHtml_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var antiforgery = GetAntiforgery(options);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => antiforgery.GetHtml(httpContext));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||
"but the current request is not an SSL request.",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChecksSSL_GetTokens_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var mockHttpContext = new Mock<HttpContext>();
|
||||
mockHttpContext.Setup(o => o.Request.IsHttps)
|
||||
.Returns(false);
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var worker = new AntiforgeryWorker(
|
||||
config: config,
|
||||
serializer: null,
|
||||
tokenStore: null,
|
||||
generator: null,
|
||||
validator: null,
|
||||
htmlEncoder: new CommonTestEncoder());
|
||||
var antiforgery = GetAntiforgery(options);
|
||||
|
||||
// Act & assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
worker.GetTokens(mockHttpContext.Object, "cookie-token"));
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => antiforgery.GetTokens(httpContext, "dkfkfkf"));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||
"but the current request is not an SSL request.",
|
||||
ex.Message);
|
||||
@"The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||
"but the current request is not an SSL request.",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
#if DNX451
|
||||
|
||||
[Fact]
|
||||
public void GetFormInputElement_ExistingInvalidCookieToken_GeneratesANewCookieAndAnAntiforgeryToken()
|
||||
{
|
||||
// Arrange
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
FormFieldName = "form-field-name"
|
||||
};
|
||||
|
||||
// Make sure the existing cookie is invalid.
|
||||
var context = GetAntiforgeryWorkerContext(config, isOldCookieValid: false);
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var context = CreateMockContext(options, isOldCookieValid: false);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
|
||||
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(
|
||||
|
|
@ -168,25 +135,25 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void GetFormInputElement_ExistingInvalidCookieToken_SwallowsExceptions()
|
||||
{
|
||||
// Arrange
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
FormFieldName = "form-field-name"
|
||||
};
|
||||
|
||||
// Make sure the existing cookie is invalid.
|
||||
var context = GetAntiforgeryWorkerContext(config, isOldCookieValid: false);
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var context = CreateMockContext(options, isOldCookieValid: false);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// This will cause the cookieToken to be null.
|
||||
context.TokenStore.Setup(o => o.GetCookieToken(context.HttpContext.Object))
|
||||
context.TokenStore.Setup(o => o.GetCookieToken(context.HttpContext))
|
||||
.Throws(new Exception("should be swallowed"));
|
||||
|
||||
// Setup so that the null cookie token returned is treated as invalid.
|
||||
context.TokenValidator.Setup(o => o.IsCookieTokenValid(null))
|
||||
context.TokenGenerator.Setup(o => o.IsCookieTokenValid(null))
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
|
||||
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(
|
||||
|
|
@ -206,11 +173,11 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
};
|
||||
|
||||
// Make sure the existing cookie is valid and use the same cookie for the mock Token Provider.
|
||||
var context = GetAntiforgeryWorkerContext(options, useOldCookie: true, isOldCookieValid: true);
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var context = CreateMockContext(options, useOldCookie: true, isOldCookieValid: true);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
|
||||
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(
|
||||
|
|
@ -231,14 +198,14 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
};
|
||||
|
||||
// Genreate a new cookie.
|
||||
var context = GetAntiforgeryWorkerContext(options, useOldCookie: false, isOldCookieValid: false);
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var context = CreateMockContext(options, useOldCookie: false, isOldCookieValid: false);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
|
||||
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
||||
|
||||
// Assert
|
||||
string xFrameOptions = context.HttpContext.Object.Response.Headers["X-Frame-Options"];
|
||||
string xFrameOptions = context.HttpContext.Response.Headers["X-Frame-Options"];
|
||||
Assert.Equal(expectedHeaderValue, xFrameOptions);
|
||||
}
|
||||
|
||||
|
|
@ -247,14 +214,14 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
{
|
||||
// Arrange
|
||||
// Genreate a new cookie.
|
||||
var context = GetAntiforgeryWorkerContext(
|
||||
var context = CreateMockContext(
|
||||
new AntiforgeryOptions(),
|
||||
useOldCookie: false,
|
||||
isOldCookieValid: false);
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
|
||||
var tokenset = antiforgery.GetTokens(context.HttpContext, "serialized-old-cookie-token");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
|
||||
|
|
@ -266,7 +233,7 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
{
|
||||
// Arrange
|
||||
// Make sure the existing cookie is invalid.
|
||||
var context = GetAntiforgeryWorkerContext(
|
||||
var context = CreateMockContext(
|
||||
new AntiforgeryOptions(),
|
||||
useOldCookie: false,
|
||||
isOldCookieValid: false);
|
||||
|
|
@ -276,12 +243,12 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
.Throws(new Exception("should be swallowed"));
|
||||
|
||||
// Setup so that the null cookie token returned is treated as invalid.
|
||||
context.TokenValidator.Setup(o => o.IsCookieTokenValid(null))
|
||||
context.TokenGenerator.Setup(o => o.IsCookieTokenValid(null))
|
||||
.Returns(false);
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
|
||||
var tokenset = antiforgery.GetTokens(context.HttpContext, "serialized-old-cookie-token");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
|
||||
|
|
@ -292,15 +259,15 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void GetTokens_ExistingValidCookieToken_GeneratesANewFormToken()
|
||||
{
|
||||
// Arrange
|
||||
var context = GetAntiforgeryWorkerContext(
|
||||
var context = CreateMockContext(
|
||||
new AntiforgeryOptions(),
|
||||
useOldCookie: true,
|
||||
isOldCookieValid: true);
|
||||
context.TokenStore = null;
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
|
||||
var tokenset = antiforgery.GetTokens(context.HttpContext, "serialized-old-cookie-token");
|
||||
|
||||
// Assert
|
||||
Assert.Null(tokenset.CookieToken);
|
||||
|
|
@ -311,95 +278,88 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void Validate_FromInvalidStrings_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var context = GetAntiforgeryWorkerContext(new AntiforgeryOptions());
|
||||
var context = CreateMockContext(new AntiforgeryOptions());
|
||||
|
||||
context.TokenSerializer.Setup(o => o.Deserialize("cookie-token"))
|
||||
.Returns(context.TestTokenSet.OldCookieToken);
|
||||
context.TokenSerializer.Setup(o => o.Deserialize("form-token"))
|
||||
.Returns(context.TestTokenSet.FormToken);
|
||||
|
||||
context.TokenValidator.Setup(o => o.ValidateTokens(
|
||||
context.HttpContext.Object,
|
||||
context.HttpContext.Object.User.Identity as ClaimsIdentity,
|
||||
context.TokenGenerator.Setup(o => o.ValidateTokens(
|
||||
context.HttpContext,
|
||||
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
|
||||
.Throws(new InvalidOperationException("my-message"));
|
||||
context.TokenStore = null;
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => worker.Validate(context.HttpContext.Object, "cookie-token", "form-token"));
|
||||
Assert.Equal("my-message", ex.Message);
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => antiforgery.Validate(context.HttpContext, "cookie-token", "form-token"));
|
||||
Assert.Equal("my-message", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_FromValidStrings_TokensValidatedSuccessfully()
|
||||
{
|
||||
// Arrange
|
||||
var context = GetAntiforgeryWorkerContext(new AntiforgeryOptions());
|
||||
var context = CreateMockContext(new AntiforgeryOptions());
|
||||
|
||||
context.TokenSerializer.Setup(o => o.Deserialize("cookie-token"))
|
||||
.Returns(context.TestTokenSet.OldCookieToken);
|
||||
context.TokenSerializer.Setup(o => o.Deserialize("form-token"))
|
||||
.Returns(context.TestTokenSet.FormToken);
|
||||
|
||||
context.TokenValidator.Setup(o => o.ValidateTokens(
|
||||
context.HttpContext.Object,
|
||||
context.HttpContext.Object.User.Identity as ClaimsIdentity,
|
||||
context.TokenGenerator.Setup(o => o.ValidateTokens(
|
||||
context.HttpContext,
|
||||
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
|
||||
.Verifiable();
|
||||
context.TokenStore = null;
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
worker.Validate(context.HttpContext.Object, "cookie-token", "form-token");
|
||||
antiforgery.Validate(context.HttpContext, "cookie-token", "form-token");
|
||||
|
||||
// Assert
|
||||
context.TokenValidator.Verify();
|
||||
context.TokenGenerator.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Validate_FromStore_Failure()
|
||||
{
|
||||
// Arrange
|
||||
var context = GetAntiforgeryWorkerContext(new AntiforgeryOptions());
|
||||
var context = CreateMockContext(new AntiforgeryOptions());
|
||||
|
||||
context.TokenValidator.Setup(o => o.ValidateTokens(
|
||||
context.HttpContext.Object,
|
||||
context.HttpContext.Object.User.Identity as ClaimsIdentity,
|
||||
context.TokenGenerator.Setup(o => o.ValidateTokens(
|
||||
context.HttpContext,
|
||||
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
|
||||
.Throws(new InvalidOperationException("my-message"));
|
||||
context.TokenSerializer = null;
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
await
|
||||
Assert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await worker.ValidateAsync(context.HttpContext.Object));
|
||||
Assert.Equal("my-message", ex.Message);
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await antiforgery.ValidateAsync(context.HttpContext));
|
||||
Assert.Equal("my-message", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Validate_FromStore_Success()
|
||||
{
|
||||
// Arrange
|
||||
var context = GetAntiforgeryWorkerContext(new AntiforgeryOptions());
|
||||
var context = CreateMockContext(new AntiforgeryOptions());
|
||||
|
||||
context.TokenValidator.Setup(o => o.ValidateTokens(
|
||||
context.HttpContext.Object,
|
||||
context.HttpContext.Object.User.Identity as ClaimsIdentity,
|
||||
context.TokenGenerator.Setup(o => o.ValidateTokens(
|
||||
context.HttpContext,
|
||||
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
|
||||
.Verifiable();
|
||||
context.TokenSerializer = null;
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
await worker.ValidateAsync(context.HttpContext.Object);
|
||||
await antiforgery.ValidateAsync(context.HttpContext);
|
||||
|
||||
// Assert
|
||||
context.TokenValidator.Verify();
|
||||
context.TokenGenerator.Verify();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -416,45 +376,55 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
};
|
||||
|
||||
// Genreate a new cookie.
|
||||
var context = GetAntiforgeryWorkerContext(options, useOldCookie: false, isOldCookieValid: false);
|
||||
var worker = GetAntiforgeryWorker(context);
|
||||
var context = CreateMockContext(options, useOldCookie: false, isOldCookieValid: false);
|
||||
var antiforgery = GetAntiforgery(context);
|
||||
|
||||
// Act
|
||||
worker.SetCookieTokenAndHeader(context.HttpContext.Object);
|
||||
antiforgery.SetCookieTokenAndHeader(context.HttpContext);
|
||||
|
||||
// Assert
|
||||
var xFrameOptions = context.HttpContext.Object.Response.Headers["X-Frame-Options"];
|
||||
var xFrameOptions = context.HttpContext.Response.Headers["X-Frame-Options"];
|
||||
Assert.Equal(expectedHeaderValue, xFrameOptions);
|
||||
}
|
||||
|
||||
private AntiforgeryWorker GetAntiforgeryWorker(AntiforgeryWorkerContext context)
|
||||
{
|
||||
return new AntiforgeryWorker(
|
||||
config: context.Options,
|
||||
serializer: context.TokenSerializer != null ? context.TokenSerializer.Object : null,
|
||||
tokenStore: context.TokenStore != null ? context.TokenStore.Object : null,
|
||||
generator: context.TokenGenerator != null ? context.TokenGenerator.Object : null,
|
||||
validator: context.TokenValidator != null ? context.TokenValidator.Object : null,
|
||||
htmlEncoder: new CommonTestEncoder());
|
||||
}
|
||||
#endif
|
||||
|
||||
private Mock<HttpContext> GetHttpContext(bool setupResponse = true)
|
||||
private Antiforgery GetAntiforgery(
|
||||
AntiforgeryOptions options = null,
|
||||
IAntiforgeryTokenGenerator tokenGenerator = null,
|
||||
IAntiforgeryTokenSerializer tokenSerializer = null,
|
||||
IAntiforgeryTokenStore tokenStore = null)
|
||||
{
|
||||
var identity = new ClaimsIdentity("some-auth");
|
||||
var mockHttpContext = new Mock<HttpContext>();
|
||||
mockHttpContext.Setup(o => o.User)
|
||||
.Returns(new ClaimsPrincipal(identity));
|
||||
|
||||
if (setupResponse)
|
||||
var optionsManager = new TestOptionsManager();
|
||||
if (options != null)
|
||||
{
|
||||
var mockResponse = new Mock<HttpResponse>();
|
||||
mockResponse.Setup(r => r.Headers)
|
||||
.Returns(new HeaderDictionary(new Dictionary<string, string[]>()));
|
||||
mockHttpContext.Setup(o => o.Response)
|
||||
.Returns(mockResponse.Object);
|
||||
optionsManager.Options = options;
|
||||
}
|
||||
|
||||
return mockHttpContext;
|
||||
return new Antiforgery(
|
||||
antiforgeryOptionsAccessor: optionsManager,
|
||||
tokenGenerator: tokenGenerator,
|
||||
tokenSerializer: tokenSerializer,
|
||||
tokenStore: tokenStore,
|
||||
htmlEncoder: new CommonTestEncoder());
|
||||
}
|
||||
|
||||
private HttpContext GetHttpContext()
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity("some-auth"));
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
#if DNX451
|
||||
|
||||
private Antiforgery GetAntiforgery(AntiforgeryMockContext context)
|
||||
{
|
||||
return GetAntiforgery(
|
||||
context.Options,
|
||||
context.TokenGenerator?.Object,
|
||||
context.TokenSerializer?.Object,
|
||||
context.TokenStore?.Object);
|
||||
}
|
||||
|
||||
private Mock<IAntiforgeryTokenStore> GetTokenStore(
|
||||
|
|
@ -495,6 +465,49 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
return mockSerializer;
|
||||
}
|
||||
|
||||
private AntiforgeryMockContext CreateMockContext(
|
||||
AntiforgeryOptions options,
|
||||
bool useOldCookie = false,
|
||||
bool isOldCookieValid = true)
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = GetHttpContext();
|
||||
var testTokenSet = GetTokenSet(isOldCookieTokenSessionToken: true, isNewCookieSessionToken: true);
|
||||
|
||||
var mockSerializer = GetTokenSerializer(testTokenSet);
|
||||
|
||||
var mockTokenStore = GetTokenStore(httpContext, testTokenSet);
|
||||
|
||||
var mockGenerator = new Mock<IAntiforgeryTokenGenerator>(MockBehavior.Strict);
|
||||
mockGenerator
|
||||
.Setup(o => o.GenerateFormToken(
|
||||
httpContext,
|
||||
useOldCookie ? testTokenSet.OldCookieToken : testTokenSet.NewCookieToken))
|
||||
.Returns(testTokenSet.FormToken);
|
||||
|
||||
mockGenerator
|
||||
.Setup(o => o.GenerateCookieToken())
|
||||
.Returns(useOldCookie ? testTokenSet.OldCookieToken : testTokenSet.NewCookieToken);
|
||||
|
||||
mockGenerator
|
||||
.Setup(o => o.IsCookieTokenValid(testTokenSet.OldCookieToken))
|
||||
.Returns(isOldCookieValid);
|
||||
|
||||
mockGenerator
|
||||
.Setup(o => o.IsCookieTokenValid(testTokenSet.NewCookieToken))
|
||||
.Returns(!isOldCookieValid);
|
||||
|
||||
return new AntiforgeryMockContext()
|
||||
{
|
||||
Options = options,
|
||||
HttpContext = httpContext,
|
||||
TokenGenerator = mockGenerator,
|
||||
TokenSerializer = mockSerializer,
|
||||
TokenStore = mockTokenStore,
|
||||
TestTokenSet = testTokenSet
|
||||
};
|
||||
}
|
||||
|
||||
private TestTokenSet GetTokenSet(bool isOldCookieTokenSessionToken = true, bool isNewCookieSessionToken = true)
|
||||
{
|
||||
return new TestTokenSet()
|
||||
|
|
@ -505,79 +518,46 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
};
|
||||
}
|
||||
|
||||
private AntiforgeryWorkerContext GetAntiforgeryWorkerContext(
|
||||
AntiforgeryOptions config,
|
||||
bool useOldCookie = false,
|
||||
bool isOldCookieValid = true)
|
||||
{
|
||||
// Arrange
|
||||
var mockHttpContext = GetHttpContext();
|
||||
var testTokenSet = GetTokenSet(isOldCookieTokenSessionToken: true, isNewCookieSessionToken: true);
|
||||
|
||||
var mockSerializer = GetTokenSerializer(testTokenSet);
|
||||
|
||||
var mockTokenStore = GetTokenStore(mockHttpContext.Object, testTokenSet);
|
||||
|
||||
var mockGenerator = new Mock<IAntiforgeryTokenGenerator>(MockBehavior.Strict);
|
||||
mockGenerator
|
||||
.Setup(o => o.GenerateFormToken(
|
||||
mockHttpContext.Object,
|
||||
mockHttpContext.Object.User.Identity as ClaimsIdentity,
|
||||
useOldCookie ? testTokenSet.OldCookieToken : testTokenSet.NewCookieToken))
|
||||
.Returns(testTokenSet.FormToken);
|
||||
|
||||
mockGenerator
|
||||
.Setup(o => o.GenerateCookieToken())
|
||||
.Returns(useOldCookie ? testTokenSet.OldCookieToken : testTokenSet.NewCookieToken);
|
||||
|
||||
var mockValidator = new Mock<IAntiforgeryTokenValidator>(MockBehavior.Strict);
|
||||
mockValidator
|
||||
.Setup(o => o.IsCookieTokenValid(testTokenSet.OldCookieToken))
|
||||
.Returns(isOldCookieValid);
|
||||
|
||||
mockValidator
|
||||
.Setup(o => o.IsCookieTokenValid(testTokenSet.NewCookieToken))
|
||||
.Returns(!isOldCookieValid);
|
||||
|
||||
return new AntiforgeryWorkerContext()
|
||||
{
|
||||
Options = config,
|
||||
HttpContext = mockHttpContext,
|
||||
TokenGenerator = mockGenerator,
|
||||
TokenValidator = mockValidator,
|
||||
TokenSerializer = mockSerializer,
|
||||
TokenStore = mockTokenStore,
|
||||
TestTokenSet = testTokenSet
|
||||
};
|
||||
}
|
||||
|
||||
private class TestTokenSet
|
||||
{
|
||||
public AntiforgeryToken FormToken { get; set; }
|
||||
|
||||
public string FormTokenString { get; set; }
|
||||
|
||||
public AntiforgeryToken OldCookieToken { get; set; }
|
||||
|
||||
public string OldCookieTokenString { get; set; }
|
||||
|
||||
public AntiforgeryToken NewCookieToken { get; set; }
|
||||
|
||||
public string NewCookieTokenString { get; set; }
|
||||
}
|
||||
|
||||
private class AntiforgeryWorkerContext
|
||||
private class AntiforgeryMockContext
|
||||
{
|
||||
public AntiforgeryOptions Options { get; set; }
|
||||
|
||||
public TestTokenSet TestTokenSet { get; set; }
|
||||
|
||||
public Mock<HttpContext> HttpContext { get; set; }
|
||||
public HttpContext HttpContext { get; set; }
|
||||
|
||||
public Mock<IAntiforgeryTokenGenerator> TokenGenerator { get; set; }
|
||||
|
||||
public Mock<IAntiforgeryTokenValidator> TokenValidator { get; set; }
|
||||
|
||||
public Mock<IAntiforgeryTokenStore> TokenStore { get; set; }
|
||||
|
||||
public Mock<IAntiforgeryTokenSerializer> TokenSerializer { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private class TestOptionsManager : IOptions<AntiforgeryOptions>
|
||||
{
|
||||
public AntiforgeryOptions Options { get; set; } = new AntiforgeryOptions();
|
||||
|
||||
public AntiforgeryOptions GetNamedOptions(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
using System;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
#if DNX451
|
||||
using Moq;
|
||||
#endif
|
||||
|
|
@ -12,44 +12,40 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
public class TokenProviderTest
|
||||
public class AntiforgeryTokenGeneratorProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void GenerateCookieToken()
|
||||
{
|
||||
// Arrange
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: null,
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
var retVal = tokenProvider.GenerateCookieToken();
|
||||
var token = tokenProvider.GenerateCookieToken();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(retVal);
|
||||
Assert.NotNull(token);
|
||||
}
|
||||
|
||||
#if DNX451
|
||||
[Fact]
|
||||
public void GenerateFormToken_AnonymousUser()
|
||||
{
|
||||
// Arrange
|
||||
var cookieToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var mockIdentity = new Mock<ClaimsIdentity>();
|
||||
mockIdentity.Setup(o => o.IsAuthenticated)
|
||||
.Returns(false);
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
Assert.False(httpContext.User.Identity.IsAuthenticated);
|
||||
|
||||
var config = new AntiforgeryOptions();
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
var fieldToken = tokenProvider.GenerateFormToken(httpContext, mockIdentity.Object, cookieToken);
|
||||
var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fieldToken);
|
||||
|
|
@ -60,6 +56,8 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
Assert.Empty(fieldToken.AdditionalData);
|
||||
}
|
||||
|
||||
#if DNX451
|
||||
|
||||
[Fact]
|
||||
public void GenerateFormToken_AuthenticatedWithoutUsernameAndNoAdditionalData_NoAdditionalData()
|
||||
{
|
||||
|
|
@ -69,20 +67,20 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
IsSessionToken = true
|
||||
};
|
||||
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
ClaimsIdentity identity = new MyAuthenticatedIdentityWithoutUsername();
|
||||
var config = new AntiforgeryOptions();
|
||||
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(new MyAuthenticatedIdentityWithoutUsername());
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var options = new AntiforgeryOptions();
|
||||
var claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: claimUidExtractor,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.GenerateFormToken(httpContext, identity, cookieToken));
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.GenerateFormToken(httpContext, cookieToken));
|
||||
Assert.Equal(
|
||||
"The provided identity of type " +
|
||||
$"'{typeof(MyAuthenticatedIdentityWithoutUsername).FullName}' " +
|
||||
|
|
@ -91,7 +89,7 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
"If it is not possible to provide a unique Name for this identity, " +
|
||||
"consider extending IAdditionalDataProvider by overriding the DefaultAdditionalDataProvider " +
|
||||
"or a custom type that can provide some form of unique identifier for the current user.",
|
||||
ex.Message);
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -99,23 +97,23 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
{
|
||||
// Arrange
|
||||
var cookieToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
ClaimsIdentity identity = new MyAuthenticatedIdentityWithoutUsername();
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(new MyAuthenticatedIdentityWithoutUsername());
|
||||
|
||||
var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>();
|
||||
mockAdditionalDataProvider.Setup(o => o.GetAdditionalData(httpContext))
|
||||
.Returns("additional-data");
|
||||
|
||||
var config = new AntiforgeryOptions();
|
||||
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
var claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: claimUidExtractor,
|
||||
additionalDataProvider: mockAdditionalDataProvider.Object);
|
||||
|
||||
// Act
|
||||
var fieldToken = tokenProvider.GenerateFormToken(httpContext, identity, cookieToken);
|
||||
var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fieldToken);
|
||||
|
|
@ -131,10 +129,10 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
{
|
||||
// Arrange
|
||||
var cookieToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var identity = GetAuthenticatedIdentity("some-identity");
|
||||
|
||||
var config = new AntiforgeryOptions();
|
||||
var identity = GetAuthenticatedIdentity("some-identity");
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(identity);
|
||||
|
||||
byte[] data = new byte[256 / 8];
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
|
|
@ -148,13 +146,13 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
|
||||
.Returns(base64ClaimUId);
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: mockClaimUidExtractor.Object,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
var fieldToken = tokenProvider.GenerateFormToken(httpContext, identity, cookieToken);
|
||||
var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fieldToken);
|
||||
|
|
@ -171,23 +169,24 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
// Arrange
|
||||
var cookieToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var mockIdentity = new Mock<ClaimsIdentity>();
|
||||
mockIdentity.Setup(o => o.IsAuthenticated)
|
||||
.Returns(true);
|
||||
mockIdentity.Setup(o => o.Name)
|
||||
.Returns("my-username");
|
||||
|
||||
var config = new AntiforgeryOptions();
|
||||
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
httpContext.User = new ClaimsPrincipal(mockIdentity.Object);
|
||||
|
||||
var claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: claimUidExtractor,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
var fieldToken = tokenProvider.GenerateFormToken(httpContext, mockIdentity.Object, cookieToken);
|
||||
var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fieldToken);
|
||||
|
|
@ -208,8 +207,8 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
IsSessionToken = false
|
||||
};
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: null,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
|
|
@ -225,16 +224,16 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
{
|
||||
// Arrange
|
||||
AntiforgeryToken cookieToken = null;
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: null,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
|
||||
var isValid = tokenProvider.IsCookieTokenValid(cookieToken);
|
||||
|
||||
// Assert
|
||||
Assert.False(retVal);
|
||||
Assert.False(isValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -246,41 +245,42 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
IsSessionToken = true
|
||||
};
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: null,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
|
||||
var isValid = tokenProvider.IsCookieTokenValid(cookieToken);
|
||||
|
||||
// Assert
|
||||
Assert.True(retVal);
|
||||
Assert.True(isValid);
|
||||
}
|
||||
|
||||
#if DNX451
|
||||
|
||||
[Fact]
|
||||
public void ValidateTokens_SessionTokenMissing()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
|
||||
AntiforgeryToken sessionToken = null;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
|
||||
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = "my-cookie-name"
|
||||
};
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
|
||||
() => tokenProvider.ValidateTokens(httpContext, null, fieldtoken));
|
||||
Assert.Equal(@"The required anti-forgery cookie ""my-cookie-name"" is not present.", ex.Message);
|
||||
}
|
||||
|
||||
|
|
@ -288,25 +288,25 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void ValidateTokens_FieldTokenMissing()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
AntiforgeryToken fieldtoken = null;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
FormFieldName = "my-form-field-name"
|
||||
};
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
|
||||
() => tokenProvider.ValidateTokens(httpContext, sessionToken, null));
|
||||
Assert.Equal(@"The required anti-forgery form field ""my-form-field-name"" is not present.", ex.Message);
|
||||
}
|
||||
|
||||
|
|
@ -314,26 +314,27 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void ValidateTokens_FieldAndSessionTokensSwapped()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = "my-cookie-name",
|
||||
FormFieldName = "my-form-field-name"
|
||||
};
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act & assert
|
||||
var ex1 =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, identity, fieldtoken, fieldtoken));
|
||||
() => tokenProvider.ValidateTokens(httpContext, fieldtoken, fieldtoken));
|
||||
Assert.Equal(
|
||||
"Validation of the provided anti-forgery token failed. " +
|
||||
@"The cookie ""my-cookie-name"" and the form field ""my-form-field-name"" were swapped.",
|
||||
|
|
@ -341,7 +342,7 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
|
||||
var ex2 =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, sessionToken));
|
||||
() => tokenProvider.ValidateTokens(httpContext, sessionToken, sessionToken));
|
||||
Assert.Equal(
|
||||
"Validation of the provided anti-forgery token failed. " +
|
||||
@"The cookie ""my-cookie-name"" and the form field ""my-form-field-name"" were swapped.",
|
||||
|
|
@ -352,23 +353,27 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void ValidateTokens_FieldAndSessionTokensHaveDifferentSecurityKeys()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: null,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
|
||||
Assert.Equal(@"The anti-forgery cookie token and form field token do not match.", ex.Message);
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery cookie token and form field token do not match.",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
#if DNX451
|
||||
|
||||
[Theory]
|
||||
[InlineData("the-user", "the-other-user")]
|
||||
[InlineData("http://example.com/uri-casing", "http://example.com/URI-casing")]
|
||||
|
|
@ -376,8 +381,10 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void ValidateTokens_UsernameMismatch(string identityUsername, string embeddedUsername)
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var identity = GetAuthenticatedIdentity(identityUsername);
|
||||
httpContext.User = new ClaimsPrincipal(identity);
|
||||
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var fieldtoken = new AntiforgeryToken()
|
||||
{
|
||||
|
|
@ -390,26 +397,28 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
|
||||
.Returns((string)null);
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: null,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: mockClaimUidExtractor.Object,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken));
|
||||
Assert.Equal(
|
||||
@"The provided anti-forgery token was meant for user """ + embeddedUsername +
|
||||
@""", but the current user is """ + identityUsername + @""".", ex.Message);
|
||||
@""", but the current user is """ + identityUsername + @""".",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateTokens_ClaimUidMismatch()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var identity = GetAuthenticatedIdentity("the-user");
|
||||
httpContext.User = new ClaimsPrincipal(identity);
|
||||
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var fieldtoken = new AntiforgeryToken()
|
||||
{
|
||||
|
|
@ -423,26 +432,27 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
|
||||
.Returns(Convert.ToBase64String(differentToken.GetData()));
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: null,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: mockClaimUidExtractor.Object,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken));
|
||||
Assert.Equal(
|
||||
@"The provided anti-forgery token was meant for a different claims-based user than the current user.",
|
||||
ex.Message);
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateTokens_AdditionalDataRejected()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var identity = new ClaimsIdentity();
|
||||
httpContext.User = new ClaimsPrincipal(identity);
|
||||
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var fieldtoken = new AntiforgeryToken()
|
||||
{
|
||||
|
|
@ -456,25 +466,25 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
|
||||
.Returns(false);
|
||||
|
||||
var config = new AntiforgeryOptions();
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: mockAdditionalDataProvider.Object);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
|
||||
Assert.Equal(@"The provided anti-forgery token failed a custom data check.", ex.Message);
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken));
|
||||
Assert.Equal(@"The provided anti-forgery token failed a custom data check.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateTokens_Success_AnonymousUser()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var identity = new ClaimsIdentity();
|
||||
httpContext.User = new ClaimsPrincipal(identity);
|
||||
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var fieldtoken = new AntiforgeryToken()
|
||||
{
|
||||
|
|
@ -488,14 +498,13 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
|
||||
.Returns(true);
|
||||
|
||||
var config = new AntiforgeryOptions();
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: mockAdditionalDataProvider.Object);
|
||||
|
||||
// Act
|
||||
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
|
||||
tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken);
|
||||
|
||||
// Assert
|
||||
// Nothing to assert - if we got this far, success!
|
||||
|
|
@ -505,8 +514,10 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void ValidateTokens_Success_AuthenticatedUserWithUsername()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var identity = GetAuthenticatedIdentity("the-user");
|
||||
httpContext.User = new ClaimsPrincipal(identity);
|
||||
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var fieldtoken = new AntiforgeryToken()
|
||||
{
|
||||
|
|
@ -520,14 +531,13 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
|
||||
.Returns(true);
|
||||
|
||||
var config = new AntiforgeryOptions();
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: new Mock<IClaimUidExtractor>().Object,
|
||||
additionalDataProvider: mockAdditionalDataProvider.Object);
|
||||
|
||||
// Act
|
||||
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
|
||||
tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken);
|
||||
|
||||
// Assert
|
||||
// Nothing to assert - if we got this far, success!
|
||||
|
|
@ -537,8 +547,10 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
public void ValidateTokens_Success_ClaimsBasedUser()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>().Object;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var identity = GetAuthenticatedIdentity("the-user");
|
||||
httpContext.User = new ClaimsPrincipal(identity);
|
||||
|
||||
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
|
||||
var fieldtoken = new AntiforgeryToken()
|
||||
{
|
||||
|
|
@ -551,15 +563,13 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
|
||||
.Returns(Convert.ToBase64String(fieldtoken.ClaimUid.GetData()));
|
||||
|
||||
var config = new AntiforgeryOptions();
|
||||
|
||||
var tokenProvider = new AntiforgeryTokenProvider(
|
||||
config: config,
|
||||
var tokenProvider = new AntiforgeryTokenGenerator(
|
||||
optionsAccessor: new TestOptionsManager(),
|
||||
claimUidExtractor: mockClaimUidExtractor.Object,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
|
||||
tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken);
|
||||
|
||||
// Assert
|
||||
// Nothing to assert - if we got this far, success!
|
||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
{
|
||||
public class AntiforgeryTokenSerializerTest
|
||||
{
|
||||
private static readonly Mock<IDataProtector> _dataProtector = GetDataProtector();
|
||||
private static readonly Mock<IDataProtectionProvider> _dataProtector = GetDataProtector();
|
||||
private static readonly BinaryBlob _claimUid = new BinaryBlob(256, new byte[] { 0x6F, 0x16, 0x48, 0xE9, 0x72, 0x49, 0xAA, 0x58, 0x75, 0x40, 0x36, 0xA6, 0x7E, 0x24, 0x8C, 0xF0, 0x44, 0xF0, 0x7E, 0xCF, 0xB0, 0xED, 0x38, 0x75, 0x56, 0xCE, 0x02, 0x9A, 0x4F, 0x9A, 0x40, 0xE0 });
|
||||
private static readonly BinaryBlob _securityToken = new BinaryBlob(128, new byte[] { 0x70, 0x5E, 0xED, 0xCC, 0x7D, 0x42, 0xF1, 0xD6, 0xB3, 0xB9, 0x8A, 0x59, 0x36, 0x25, 0xBB, 0x4C });
|
||||
private const byte _salt = 0x05;
|
||||
|
|
@ -138,7 +138,7 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
_dataProtector.Verify();
|
||||
}
|
||||
|
||||
private static Mock<IDataProtector> GetDataProtector()
|
||||
private static Mock<IDataProtectionProvider> GetDataProtector()
|
||||
{
|
||||
var mockCryptoSystem = new Mock<IDataProtector>();
|
||||
mockCryptoSystem.Setup(o => o.Protect(It.IsAny<byte[]>()))
|
||||
|
|
@ -147,7 +147,12 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockCryptoSystem.Setup(o => o.Unprotect(It.IsAny<byte[]>()))
|
||||
.Returns<byte[]>(UnProtect)
|
||||
.Verifiable();
|
||||
return mockCryptoSystem;
|
||||
|
||||
var provider = new Mock<IDataProtectionProvider>();
|
||||
provider
|
||||
.Setup(p => p.CreateProtector(It.IsAny<string>()))
|
||||
.Returns(mockCryptoSystem.Object);
|
||||
return provider;
|
||||
}
|
||||
|
||||
private static byte[] Protect(byte[] data)
|
||||
|
|
|
|||
|
|
@ -32,14 +32,14 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
var contextAccessor = new AntiforgeryContextAccessor();
|
||||
mockHttpContext.SetupGet(o => o.RequestServices)
|
||||
.Returns(GetServiceProvider(contextAccessor));
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = _cookieName
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: null);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: null);
|
||||
|
||||
// Act
|
||||
var token = tokenStore.GetCookieToken(mockHttpContext.Object);
|
||||
|
|
@ -67,14 +67,14 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
// add a cookie explicitly.
|
||||
var cookie = new AntiforgeryToken();
|
||||
contextAccessor.Value = new AntiforgeryContext() { CookieToken = cookie };
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = _cookieName
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: null);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: null);
|
||||
|
||||
// Act
|
||||
var token = tokenStore.GetCookieToken(mockHttpContext.Object);
|
||||
|
|
@ -89,14 +89,14 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
// Arrange
|
||||
var mockHttpContext = GetMockHttpContext(_cookieName, string.Empty);
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = _cookieName
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: null);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: null);
|
||||
|
||||
// Act
|
||||
var token = tokenStore.GetCookieToken(mockHttpContext);
|
||||
|
|
@ -110,10 +110,6 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
{
|
||||
// Arrange
|
||||
var mockHttpContext = GetMockHttpContext(_cookieName, "invalid-value");
|
||||
var config = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = _cookieName
|
||||
};
|
||||
|
||||
var expectedException = new InvalidOperationException("some exception");
|
||||
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
|
||||
|
|
@ -121,9 +117,14 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
.Setup(o => o.Deserialize("invalid-value"))
|
||||
.Throws(expectedException);
|
||||
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = _cookieName
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: mockSerializer.Object);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: mockSerializer.Object);
|
||||
|
||||
// Act & assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => tokenStore.GetCookieToken(mockHttpContext));
|
||||
|
|
@ -137,19 +138,19 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
var expectedToken = new AntiforgeryToken();
|
||||
var mockHttpContext = GetMockHttpContext(_cookieName, "valid-value");
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = _cookieName
|
||||
};
|
||||
|
||||
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
|
||||
mockSerializer
|
||||
.Setup(o => o.Deserialize("valid-value"))
|
||||
.Returns(expectedToken);
|
||||
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = _cookieName
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: mockSerializer.Object);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: mockSerializer.Object);
|
||||
|
||||
// Act
|
||||
AntiforgeryToken retVal = tokenStore.GetCookieToken(mockHttpContext);
|
||||
|
|
@ -170,14 +171,15 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
.Returns(Task.FromResult(formCollection.Object));
|
||||
mockHttpContext.Setup(o => o.Request)
|
||||
.Returns(requestContext.Object);
|
||||
var config = new AntiforgeryOptions()
|
||||
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
FormFieldName = "form-field-name"
|
||||
FormFieldName = "form-field-name",
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: null);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: null);
|
||||
|
||||
// Act
|
||||
var token = await tokenStore.GetFormTokenAsync(mockHttpContext.Object);
|
||||
|
|
@ -201,26 +203,24 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockHttpContext.Setup(o => o.Request)
|
||||
.Returns(requestContext.Object);
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
{
|
||||
FormFieldName = "form-field-name"
|
||||
};
|
||||
|
||||
var expectedException = new InvalidOperationException("some exception");
|
||||
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
|
||||
mockSerializer.Setup(o => o.Deserialize("invalid-value"))
|
||||
.Throws(expectedException);
|
||||
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
FormFieldName = "form-field-name",
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: mockSerializer.Object);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: mockSerializer.Object);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
await
|
||||
Assert.ThrowsAsync<InvalidOperationException>(
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await tokenStore.GetFormTokenAsync(mockHttpContext.Object));
|
||||
Assert.Same(expectedException, ex);
|
||||
Assert.Same(expectedException, exception);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -239,18 +239,18 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockHttpContext.Setup(o => o.Request)
|
||||
.Returns(requestContext.Object);
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
{
|
||||
FormFieldName = "form-field-name"
|
||||
};
|
||||
|
||||
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
|
||||
mockSerializer.Setup(o => o.Deserialize("valid-value"))
|
||||
.Returns(expectedToken);
|
||||
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
FormFieldName = "form-field-name",
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: mockSerializer.Object);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: mockSerializer.Object);
|
||||
|
||||
// Act
|
||||
var retVal = await tokenStore.GetFormTokenAsync(mockHttpContext.Object);
|
||||
|
|
@ -283,15 +283,15 @@ namespace Microsoft.AspNet.Antiforgery
|
|||
mockSerializer.Setup(o => o.Serialize(token))
|
||||
.Returns("serialized-value");
|
||||
|
||||
var config = new AntiforgeryOptions()
|
||||
var options = new AntiforgeryOptions()
|
||||
{
|
||||
CookieName = _cookieName,
|
||||
RequireSSL = requireSsl
|
||||
};
|
||||
|
||||
var tokenStore = new AntiforgeryTokenStore(
|
||||
config: config,
|
||||
serializer: mockSerializer.Object);
|
||||
optionsAccessor: new TestOptionsManager(options),
|
||||
tokenSerializer: mockSerializer.Object);
|
||||
|
||||
// Act
|
||||
tokenStore.SaveCookieToken(mockHttpContext.Object, token);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Antiforgery
|
||||
{
|
||||
public class TestOptionsManager : IOptions<AntiforgeryOptions>
|
||||
{
|
||||
public TestOptionsManager()
|
||||
{
|
||||
}
|
||||
|
||||
public TestOptionsManager(AntiforgeryOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public AntiforgeryOptions Options { get; set; } = new AntiforgeryOptions();
|
||||
|
||||
public AntiforgeryOptions GetNamedOptions(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue