This commit is contained in:
Ryan Nowak 2015-06-08 14:28:39 -07:00
Родитель 68ee820b5d
Коммит 3dc2663c35
33 изменённых файлов: 3897 добавлений и 16 удалений

Просмотреть файл

@ -0,0 +1,139 @@
// 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.Linq;
using System.Security.Cryptography;
using System.Text;
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;
namespace Microsoft.AspNet.Antiforgery
{
/// <summary>
/// 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
{
private static readonly string _purpose = "Microsoft.AspNet.Antiforgery.AntiforgeryToken.v1";
private readonly AntiforgeryWorker _worker;
public Antiforgery(
[NotNull] IClaimUidExtractor claimUidExtractor,
[NotNull] IDataProtectionProvider dataProtectionProvider,
[NotNull] IAntiforgeryAdditionalDataProvider additionalDataProvider,
[NotNull] IOptions<AntiforgeryOptions> AntiforgeryOptionsAccessor,
[NotNull] IHtmlEncoder htmlEncoder,
[NotNull] IOptions<DataProtectionOptions> dataProtectionOptions)
{
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);
}
/// <summary>
/// Generates an anti-forgery token for this request. This token can
/// be validated by calling the Validate() method.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
/// <returns>An HTML string corresponding to an &lt;input type="hidden"&gt;
/// element. This element should be put inside a &lt;form&gt;.</returns>
/// <remarks>
/// This method has a side effect:
/// A response cookie is set if there is no valid cookie associated with the request.
/// </remarks>
public string GetHtml([NotNull] HttpContext context)
{
var html = _worker.GetFormInputElement(context);
return html;
}
/// <summary>
/// Generates an anti-forgery token pair (cookie and form token) for this request.
/// This method is similar to GetHtml(HttpContext context), but this method gives the caller control
/// over how to persist the returned values. To validate these tokens, call the
/// appropriate overload of Validate.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
/// <param name="oldCookieToken">The anti-forgery token - if any - that already existed
/// for this request. May be null. The anti-forgery system will try to reuse this cookie
/// value when generating a matching form token.</param>
/// <remarks>
/// Unlike the GetHtml(HttpContext context) method, this method has no side effect. The caller
/// is responsible for setting the response cookie and injecting the returned
/// form token as appropriate.
/// </remarks>
public AntiforgeryTokenSet GetTokens([NotNull] HttpContext context, string oldCookieToken)
{
// Will contain a new cookie value if the old cookie token
// was null or invalid. If this value is non-null when the method completes, the caller
// 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);
}
/// <summary>
/// Validates an anti-forgery token that was supplied for this request.
/// The anti-forgery token may be generated by calling GetHtml(HttpContext context).
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
public async Task ValidateAsync([NotNull] HttpContext context)
{
await _worker.ValidateAsync(context);
}
/// <summary>
/// Validates an anti-forgery token pair that was generated by the GetTokens method.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
/// <param name="cookieToken">The token that was supplied in the request cookie.</param>
/// <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);
}
/// <summary>
/// Validates an anti-forgery token pair that was generated by the GetTokens method.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
/// <param name="AntiforgeryTokenSet">The anti-forgery token pair (cookie and form token) for this request.
/// </param>
public void Validate([NotNull] HttpContext context, AntiforgeryTokenSet AntiforgeryTokenSet)
{
Validate(context, AntiforgeryTokenSet.CookieToken, AntiforgeryTokenSet.FormToken);
}
/// <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 context)
{
_worker.SetCookieTokenAndHeader(context);
}
private 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);
}
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
// 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.
namespace Microsoft.AspNet.Antiforgery
{
/// <summary>
/// Used as a per request state.
/// </summary>
public class AntiforgeryContext
{
public AntiforgeryToken CookieToken { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// 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.
namespace Microsoft.AspNet.Antiforgery
{
public class AntiforgeryContextAccessor : IAntiforgeryContextAccessor
{
public AntiforgeryContext Value { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,81 @@
// 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.Internal;
namespace Microsoft.AspNet.Antiforgery
{
/// <summary>
/// Provides programmatic configuration for the anti-forgery token system.
/// </summary>
public class AntiforgeryOptions
{
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.
/// </summary>
/// <remarks>
/// 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;
}
}
/// <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;
}
}
/// <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;
}
/// <summary>
/// Specifies whether to suppress the generation of X-Frame-Options header
/// which is used to prevent ClickJacking. By default, the X-Frame-Options
/// 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;
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
// 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.
namespace Microsoft.AspNet.Antiforgery
{
public sealed class AntiforgeryToken
{
internal const int SecurityTokenBitLength = 128;
internal const int ClaimUidBitLength = 256;
private string _additionalData = string.Empty;
private string _username = string.Empty;
private BinaryBlob _securityToken;
public string AdditionalData
{
get { return _additionalData; }
set
{
_additionalData = value ?? string.Empty;
}
}
public BinaryBlob ClaimUid { get; set; }
public bool IsSessionToken { get; set; }
public BinaryBlob SecurityToken
{
get
{
if (_securityToken == null)
{
_securityToken = new BinaryBlob(SecurityTokenBitLength);
}
return _securityToken;
}
set
{
_securityToken = value;
}
}
public string Username
{
get { return _username; }
set
{
_username = value ?? string.Empty;
}
}
}
}

Просмотреть файл

@ -0,0 +1,168 @@
// 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 Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Antiforgery
{
public sealed class AntiforgeryTokenProvider : IAntiforgeryTokenValidator, IAntiforgeryTokenGenerator
{
private readonly IClaimUidExtractor _claimUidExtractor;
private readonly AntiforgeryOptions _config;
private readonly IAntiforgeryAdditionalDataProvider _additionalDataProvider;
internal AntiforgeryTokenProvider(
AntiforgeryOptions config,
IClaimUidExtractor claimUidExtractor,
IAntiforgeryAdditionalDataProvider additionalDataProvider)
{
_config = config;
_claimUidExtractor = claimUidExtractor;
_additionalDataProvider = additionalDataProvider;
}
public AntiforgeryToken GenerateCookieToken()
{
return new AntiforgeryToken()
{
// SecurityToken will be populated automatically.
IsSessionToken = true
};
}
public AntiforgeryToken GenerateFormToken(HttpContext httpContext,
ClaimsIdentity identity,
AntiforgeryToken cookieToken)
{
Debug.Assert(IsCookieTokenValid(cookieToken));
var formToken = new AntiforgeryToken()
{
SecurityToken = cookieToken.SecurityToken,
IsSessionToken = false
};
var isIdentityAuthenticated = false;
// populate Username and ClaimUid
if (identity != null && identity.IsAuthenticated)
{
isIdentityAuthenticated = true;
formToken.ClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity));
if (formToken.ClaimUid == null)
{
formToken.Username = identity.Name;
}
}
// populate AdditionalData
if (_additionalDataProvider != null)
{
formToken.AdditionalData = _additionalDataProvider.GetAdditionalData(httpContext);
}
if (isIdentityAuthenticated
&& string.IsNullOrEmpty(formToken.Username)
&& formToken.ClaimUid == null
&& string.IsNullOrEmpty(formToken.AdditionalData))
{
// Application says user is authenticated, but we have no identifier for the user.
throw new InvalidOperationException(
Resources.FormatAntiforgeryTokenValidator_AuthenticatedUserWithoutUsername(identity.GetType()));
}
return formToken;
}
public bool IsCookieTokenValid(AntiforgeryToken cookieToken)
{
return (cookieToken != null && cookieToken.IsSessionToken);
}
public void ValidateTokens(
HttpContext httpContext,
ClaimsIdentity identity,
AntiforgeryToken sessionToken,
AntiforgeryToken fieldToken)
{
// Were the tokens even present at all?
if (sessionToken == null)
{
throw new InvalidOperationException(
Resources.FormatAntiforgeryToken_CookieMissing(_config.CookieName));
}
if (fieldToken == null)
{
throw new InvalidOperationException(
Resources.FormatAntiforgeryToken_FormFieldMissing(_config.FormFieldName));
}
// Do the tokens have the correct format?
if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken)
{
throw new InvalidOperationException(
Resources.FormatAntiforgeryToken_TokensSwapped(_config.CookieName, _config.FormFieldName));
}
// Are the security tokens embedded in each incoming token identical?
if (!Equals(sessionToken.SecurityToken, fieldToken.SecurityToken))
{
throw new InvalidOperationException(Resources.AntiforgeryToken_SecurityTokenMismatch);
}
// Is the incoming token meant for the current user?
var currentUsername = string.Empty;
BinaryBlob currentClaimUid = null;
if (identity != null && identity.IsAuthenticated)
{
currentClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity));
if (currentClaimUid == null)
{
currentUsername = identity.Name ?? string.Empty;
}
}
// OpenID and other similar authentication schemes use URIs for the username.
// These should be treated as case-sensitive.
var useCaseSensitiveUsernameComparison =
currentUsername.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
currentUsername.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
if (!string.Equals(fieldToken.Username,
currentUsername,
(useCaseSensitiveUsernameComparison) ?
StringComparison.Ordinal :
StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
Resources.FormatAntiforgeryToken_UsernameMismatch(fieldToken.Username, currentUsername));
}
if (!Equals(fieldToken.ClaimUid, currentClaimUid))
{
throw new InvalidOperationException(Resources.AntiforgeryToken_ClaimUidMismatch);
}
// Is the AdditionalData valid?
if (_additionalDataProvider != null &&
!_additionalDataProvider.ValidateAdditionalData(httpContext, fieldToken.AdditionalData))
{
throw new InvalidOperationException(Resources.AntiforgeryToken_AdditionalDataCheckFailed);
}
}
private static BinaryBlob GetClaimUidBlob(string base64ClaimUid)
{
if (base64ClaimUid == null)
{
return null;
}
return new BinaryBlob(256, Convert.FromBase64String(base64ClaimUid));
}
}
}

Просмотреть файл

@ -0,0 +1,135 @@
// 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.IO;
using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Antiforgery
{
public sealed class AntiforgeryTokenSerializer : IAntiforgeryTokenSerializer
{
private readonly IDataProtector _cryptoSystem;
private const byte TokenVersion = 0x01;
public AntiforgeryTokenSerializer([NotNull] IDataProtector cryptoSystem)
{
_cryptoSystem = cryptoSystem;
}
public AntiforgeryToken Deserialize(string serializedToken)
{
Exception innerException = null;
try
{
var tokenBytes = WebEncoders.Base64UrlDecode(serializedToken);
using (var stream = new MemoryStream(_cryptoSystem.Unprotect(tokenBytes)))
{
using (var reader = new BinaryReader(stream))
{
var token = DeserializeImpl(reader);
if (token != null)
{
return token;
}
}
}
}
catch (Exception ex)
{
// swallow all exceptions - homogenize error if something went wrong
innerException = ex;
}
// if we reached this point, something went wrong deserializing
throw new InvalidOperationException(Resources.AntiforgeryToken_DeserializationFailed, innerException);
}
/* The serialized format of the anti-XSRF token is as follows:
* Version: 1 byte integer
* SecurityToken: 16 byte binary blob
* IsSessionToken: 1 byte Boolean
* [if IsSessionToken != true]
* +- IsClaimsBased: 1 byte Boolean
* | [if IsClaimsBased = true]
* | `- ClaimUid: 32 byte binary blob
* | [if IsClaimsBased = false]
* | `- Username: UTF-8 string with 7-bit integer length prefix
* `- AdditionalData: UTF-8 string with 7-bit integer length prefix
*/
private static AntiforgeryToken DeserializeImpl(BinaryReader reader)
{
// we can only consume tokens of the same serialized version that we generate
var embeddedVersion = reader.ReadByte();
if (embeddedVersion != TokenVersion)
{
return null;
}
var deserializedToken = new AntiforgeryToken();
var securityTokenBytes = reader.ReadBytes(AntiforgeryToken.SecurityTokenBitLength / 8);
deserializedToken.SecurityToken =
new BinaryBlob(AntiforgeryToken.SecurityTokenBitLength, securityTokenBytes);
deserializedToken.IsSessionToken = reader.ReadBoolean();
if (!deserializedToken.IsSessionToken)
{
var isClaimsBased = reader.ReadBoolean();
if (isClaimsBased)
{
var claimUidBytes = reader.ReadBytes(AntiforgeryToken.ClaimUidBitLength / 8);
deserializedToken.ClaimUid = new BinaryBlob(AntiforgeryToken.ClaimUidBitLength, claimUidBytes);
}
else
{
deserializedToken.Username = reader.ReadString();
}
deserializedToken.AdditionalData = reader.ReadString();
}
// if there's still unconsumed data in the stream, fail
if (reader.BaseStream.ReadByte() != -1)
{
return null;
}
// success
return deserializedToken;
}
public string Serialize([NotNull] AntiforgeryToken token)
{
using (var stream = new MemoryStream())
{
using (var writer = new BinaryWriter(stream))
{
writer.Write(TokenVersion);
writer.Write(token.SecurityToken.GetData());
writer.Write(token.IsSessionToken);
if (!token.IsSessionToken)
{
if (token.ClaimUid != null)
{
writer.Write(true /* isClaimsBased */);
writer.Write(token.ClaimUid.GetData());
}
else
{
writer.Write(false /* isClaimsBased */);
writer.Write(token.Username);
}
writer.Write(token.AdditionalData);
}
writer.Flush();
return WebEncoders.Base64UrlEncode(_cryptoSystem.Protect(stream.ToArray()));
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,42 @@
// 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;
namespace Microsoft.AspNet.Antiforgery
{
/// <summary>
/// The anti-forgery token pair (cookie and form token) for a request.
/// </summary>
public class AntiforgeryTokenSet
{
/// <summary>
/// Creates the anti-forgery token pair (cookie and form token) for a request.
/// </summary>
/// <param name="formToken">The token that is supplied in the request form body.</param>
/// <param name="cookieToken">The token that is supplied in the request cookie.</param>
public AntiforgeryTokenSet(string formToken, string cookieToken)
{
if (string.IsNullOrEmpty(formToken))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(formToken));
}
FormToken = formToken;
// Cookie Token is allowed to be null in the case when the old cookie is valid
// and there is no new cookieToken generated.
CookieToken = cookieToken;
}
/// <summary>
/// The token that is supplied in the request form body.
/// </summary>
public string FormToken { get; private set; }
/// The cookie token is allowed to be null.
/// This would be the case when the old cookie token is still valid.
/// In such cases a call to GetTokens would return a token set with null cookie token.
public string CookieToken { get; private set; }
}
}

Просмотреть файл

@ -0,0 +1,79 @@
// 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.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Antiforgery
{
// Saves anti-XSRF tokens split between HttpRequest.Cookies and HttpRequest.Form
public sealed class AntiforgeryTokenStore : IAntiforgeryTokenStore
{
private readonly AntiforgeryOptions _config;
private readonly IAntiforgeryTokenSerializer _serializer;
public AntiforgeryTokenStore([NotNull] AntiforgeryOptions config,
[NotNull] IAntiforgeryTokenSerializer serializer)
{
_config = config;
_serializer = serializer;
}
public AntiforgeryToken GetCookieToken(HttpContext httpContext)
{
var contextAccessor =
httpContext.RequestServices.GetRequiredService<IAntiforgeryContextAccessor>();
if (contextAccessor.Value != null)
{
return contextAccessor.Value.CookieToken;
}
var requestCookie = httpContext.Request.Cookies[_config.CookieName];
if (string.IsNullOrEmpty(requestCookie))
{
// unable to find the cookie.
return null;
}
return _serializer.Deserialize(requestCookie);
}
public async Task<AntiforgeryToken> GetFormTokenAsync(HttpContext httpContext)
{
var form = await httpContext.Request.ReadFormAsync();
var value = form[_config.FormFieldName];
if (string.IsNullOrEmpty(value))
{
// did not exist
return null;
}
return _serializer.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>();
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 options = new CookieOptions() { HttpOnly = true };
// Note: don't use "newCookie.Secure = _config.RequireSSL;" since the default
// value of newCookie.Secure is poulated out of band.
if (_config.RequireSSL)
{
options.Secure = true;
}
httpContext.Response.Cookies.Append(_config.CookieName, serializedToken, options);
}
}
}

Просмотреть файл

@ -0,0 +1,117 @@
// 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.Globalization;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace Microsoft.AspNet.Antiforgery
{
// Represents a binary blob (token) that contains random data.
// Useful for binary data inside a serialized stream.
[DebuggerDisplay("{DebuggerString}")]
public sealed class BinaryBlob : IEquatable<BinaryBlob>
{
private static readonly RandomNumberGenerator _randomNumberGenerator = RandomNumberGenerator.Create();
private readonly byte[] _data;
// Generates a new token using a specified bit length.
public BinaryBlob(int bitLength)
: this(bitLength, GenerateNewToken(bitLength))
{
}
// Generates a token using an existing binary value.
public BinaryBlob(int bitLength, byte[] data)
{
if (bitLength < 32 || bitLength % 8 != 0)
{
throw new ArgumentOutOfRangeException("bitLength");
}
if (data == null || data.Length != bitLength / 8)
{
throw new ArgumentOutOfRangeException("data");
}
_data = data;
}
public int BitLength
{
get
{
return checked(_data.Length * 8);
}
}
private string DebuggerString
{
get
{
var sb = new StringBuilder("0x", 2 + (_data.Length * 2));
for (var i = 0; i < _data.Length; i++)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0:x2}", _data[i]);
}
return sb.ToString();
}
}
public override bool Equals(object obj)
{
return Equals(obj as BinaryBlob);
}
public bool Equals(BinaryBlob other)
{
if (other == null)
{
return false;
}
Debug.Assert(this._data.Length == other._data.Length);
return AreByteArraysEqual(this._data, other._data);
}
public byte[] GetData()
{
return _data;
}
public override int GetHashCode()
{
// Since data should contain uniformly-distributed entropy, the
// first 32 bits can serve as the hash code.
Debug.Assert(_data != null && _data.Length >= (32 / 8));
return BitConverter.ToInt32(_data, 0);
}
private static byte[] GenerateNewToken(int bitLength)
{
var data = new byte[bitLength / 8];
_randomNumberGenerator.GetBytes(data);
return data;
}
// Need to mark it with NoInlining and NoOptimization attributes to ensure that the
// operation runs in constant time.
[MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static bool AreByteArraysEqual(byte[] a, byte[] b)
{
if (a == null || b == null || a.Length != b.Length)
{
return false;
}
var areEqual = true;
for (var i = 0; i < a.Length; i++)
{
areEqual &= (a[i] == b[i]);
}
return areEqual;
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
// 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.AspNet.Http;
namespace Microsoft.AspNet.Antiforgery
{
/// <summary>
/// A default <see cref="IAntiforgeryAdditionalDataProvider"/> implementation.
/// </summary>
public class DefaultAntiforgeryAdditionalDataProvider : IAntiforgeryAdditionalDataProvider
{
/// <inheritdoc />
public virtual string GetAdditionalData(HttpContext context)
{
return string.Empty;
}
/// <inheritdoc />
public virtual bool ValidateAdditionalData(HttpContext context, string additionalData)
{
// Default implementation does not understand anything but empty data.
return string.IsNullOrEmpty(additionalData);
}
}
}

Просмотреть файл

@ -0,0 +1,82 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
namespace Microsoft.AspNet.Antiforgery
{
/// <summary>
/// Default implementation of <see cref="IClaimUidExtractor"/>.
/// </summary>
public class DefaultClaimUidExtractor : IClaimUidExtractor
{
/// <inheritdoc />
public string ExtractClaimUid(ClaimsIdentity claimsIdentity)
{
if (claimsIdentity == null || !claimsIdentity.IsAuthenticated)
{
// Skip anonymous users
return null;
}
var uniqueIdentifierParameters = GetUniqueIdentifierParameters(claimsIdentity);
var claimUidBytes = ComputeSHA256(uniqueIdentifierParameters);
return Convert.ToBase64String(claimUidBytes);
}
// Internal for testing
internal static IEnumerable<string> GetUniqueIdentifierParameters(ClaimsIdentity claimsIdentity)
{
var nameIdentifierClaim = claimsIdentity.FindFirst(claim =>
String.Equals(ClaimTypes.NameIdentifier,
claim.Type, StringComparison.Ordinal));
if (nameIdentifierClaim != null && !string.IsNullOrEmpty(nameIdentifierClaim.Value))
{
return new string[]
{
ClaimTypes.NameIdentifier,
nameIdentifierClaim.Value
};
}
// We do not understand this ClaimsIdentity, fallback on serializing the entire claims Identity.
var claims = claimsIdentity.Claims.ToList();
claims.Sort((a, b) => string.Compare(a.Type, b.Type, StringComparison.Ordinal));
var identifierParameters = new List<string>();
foreach (var claim in claims)
{
identifierParameters.Add(claim.Type);
identifierParameters.Add(claim.Value);
}
return identifierParameters;
}
private static byte[] ComputeSHA256(IEnumerable<string> parameters)
{
using (var stream = new MemoryStream())
{
using (var writer = new BinaryWriter(stream))
{
foreach (string parameter in parameters)
{
writer.Write(parameter); // also writes the length as a prefix; unambiguous
}
writer.Flush();
using (var sha256 = SHA256.Create())
{
var bytes = sha256.ComputeHash(stream.ToArray(), 0, checked((int)stream.Length));
return bytes;
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,39 @@
// 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.AspNet.Http;
namespace Microsoft.AspNet.Antiforgery
{
/// <summary>
/// Allows providing or validating additional custom data for anti-forgery tokens.
/// For example, the developer could use this to supply a nonce when the token is
/// generated, then he could validate the nonce when the token is validated.
/// </summary>
/// <remarks>
/// The anti-forgery system already embeds the client's username within the
/// generated tokens. This interface provides and consumes <em>supplemental</em>
/// data. If an incoming anti-forgery token contains supplemental data but no
/// additional data provider is configured, the supplemental data will not be
/// validated.
/// </remarks>
public interface IAntiforgeryAdditionalDataProvider
{
/// <summary>
/// Provides additional data to be stored for the anti-forgery tokens generated
/// during this request.
/// </summary>
/// <param name="context">Information about the current request.</param>
/// <returns>Supplemental data to embed within the anti-forgery token.</returns>
string GetAdditionalData(HttpContext context);
/// <summary>
/// Validates additional data that was embedded inside an incoming anti-forgery
/// token.
/// </summary>
/// <param name="context">Information about the current request.</param>
/// <param name="additionalData">Supplemental data that was embedded within the token.</param>
/// <returns>True if the data is valid; false if the data is invalid.</returns>
bool ValidateAdditionalData(HttpContext context, string additionalData);
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// 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.
namespace Microsoft.AspNet.Antiforgery
{
public interface IAntiforgeryContextAccessor
{
AntiforgeryContext Value { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// 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 configuration information about the anti-forgery system.
public interface IAntiforgeryTokenGenerator
{
// Generates a new random cookie token.
AntiforgeryToken GenerateCookieToken();
// Given a cookie token, generates a corresponding form token.
// The incoming cookie token must be valid.
AntiforgeryToken GenerateFormToken(
HttpContext httpContext,
ClaimsIdentity identity,
AntiforgeryToken cookieToken);
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// 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.
namespace Microsoft.AspNet.Antiforgery
{
// Abstracts out the serialization process for an anti-forgery token
public interface IAntiforgeryTokenSerializer
{
AntiforgeryToken Deserialize(string serializedToken);
string Serialize(AntiforgeryToken token);
}
}

Просмотреть файл

@ -0,0 +1,16 @@
// 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.Threading.Tasks;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Antiforgery
{
// Provides an abstraction around how tokens are persisted and retrieved for a request
public interface IAntiforgeryTokenStore
{
AntiforgeryToken GetCookieToken(HttpContext httpContext);
Task<AntiforgeryToken> GetFormTokenAsync(HttpContext httpContext);
void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token);
}
}

Просмотреть файл

@ -0,0 +1,23 @@
// 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);
}
}

Просмотреть файл

@ -0,0 +1,20 @@
// 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;
namespace Microsoft.AspNet.Antiforgery
{
/// <summary>
/// This interface can extract unique identifers for a claims-based identity.
/// </summary>
public interface IClaimUidExtractor
{
/// <summary>
/// Extracts claims identifier.
/// </summary>
/// <param name="identity">The <see cref="ClaimsIdentity"/>.</param>
/// <returns>The claims identifier.</returns>
string ExtractClaimUid(ClaimsIdentity identity);
}
}

Просмотреть файл

@ -0,0 +1,251 @@
// 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; }
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
// 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.Reflection;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("Microsoft.AspNet.Antiforgery.Test")]
[assembly: AssemblyMetadata("Serviceable", "True")]

206
src/Microsoft.AspNet.Antiforgery/Properties/Resources.Designer.cs сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,206 @@
// <auto-generated />
namespace Microsoft.AspNet.Antiforgery
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNet.Antiforgery.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// The provided identity of type '{0}' is marked IsAuthenticated = true but does not have a value for Name. By default, the anti-forgery system requires that all authenticated identities have a unique Name. 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.
/// </summary>
internal static string AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername
{
get { return GetString("AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername"); }
}
/// <summary>
/// The provided identity of type '{0}' is marked IsAuthenticated = true but does not have a value for Name. By default, the anti-forgery system requires that all authenticated identities have a unique Name. 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.
/// </summary>
internal static string FormatAntiforgeryTokenValidator_AuthenticatedUserWithoutUsername(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername"), p0);
}
/// <summary>
/// The provided anti-forgery token failed a custom data check.
/// </summary>
internal static string AntiforgeryToken_AdditionalDataCheckFailed
{
get { return GetString("AntiforgeryToken_AdditionalDataCheckFailed"); }
}
/// <summary>
/// The provided anti-forgery token failed a custom data check.
/// </summary>
internal static string FormatAntiforgeryToken_AdditionalDataCheckFailed()
{
return GetString("AntiforgeryToken_AdditionalDataCheckFailed");
}
/// <summary>
/// The provided anti-forgery token was meant for a different claims-based user than the current user.
/// </summary>
internal static string AntiforgeryToken_ClaimUidMismatch
{
get { return GetString("AntiforgeryToken_ClaimUidMismatch"); }
}
/// <summary>
/// The provided anti-forgery token was meant for a different claims-based user than the current user.
/// </summary>
internal static string FormatAntiforgeryToken_ClaimUidMismatch()
{
return GetString("AntiforgeryToken_ClaimUidMismatch");
}
/// <summary>
/// The required anti-forgery cookie "{0}" is not present.
/// </summary>
internal static string AntiforgeryToken_CookieMissing
{
get { return GetString("AntiforgeryToken_CookieMissing"); }
}
/// <summary>
/// The required anti-forgery cookie "{0}" is not present.
/// </summary>
internal static string FormatAntiforgeryToken_CookieMissing(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_CookieMissing"), p0);
}
/// <summary>
/// The anti-forgery token could not be decrypted.
/// </summary>
internal static string AntiforgeryToken_DeserializationFailed
{
get { return GetString("AntiforgeryToken_DeserializationFailed"); }
}
/// <summary>
/// The anti-forgery token could not be decrypted.
/// </summary>
internal static string FormatAntiforgeryToken_DeserializationFailed()
{
return GetString("AntiforgeryToken_DeserializationFailed");
}
/// <summary>
/// The required anti-forgery form field "{0}" is not present.
/// </summary>
internal static string AntiforgeryToken_FormFieldMissing
{
get { return GetString("AntiforgeryToken_FormFieldMissing"); }
}
/// <summary>
/// The required anti-forgery form field "{0}" is not present.
/// </summary>
internal static string FormatAntiforgeryToken_FormFieldMissing(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_FormFieldMissing"), p0);
}
/// <summary>
/// The anti-forgery cookie token and form field token do not match.
/// </summary>
internal static string AntiforgeryToken_SecurityTokenMismatch
{
get { return GetString("AntiforgeryToken_SecurityTokenMismatch"); }
}
/// <summary>
/// The anti-forgery cookie token and form field token do not match.
/// </summary>
internal static string FormatAntiforgeryToken_SecurityTokenMismatch()
{
return GetString("AntiforgeryToken_SecurityTokenMismatch");
}
/// <summary>
/// Validation of the provided anti-forgery token failed. The cookie "{0}" and the form field "{1}" were swapped.
/// </summary>
internal static string AntiforgeryToken_TokensSwapped
{
get { return GetString("AntiforgeryToken_TokensSwapped"); }
}
/// <summary>
/// Validation of the provided anti-forgery token failed. The cookie "{0}" and the form field "{1}" were swapped.
/// </summary>
internal static string FormatAntiforgeryToken_TokensSwapped(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_TokensSwapped"), p0, p1);
}
/// <summary>
/// The provided anti-forgery token was meant for user "{0}", but the current user is "{1}".
/// </summary>
internal static string AntiforgeryToken_UsernameMismatch
{
get { return GetString("AntiforgeryToken_UsernameMismatch"); }
}
/// <summary>
/// The provided anti-forgery token was meant for user "{0}", but the current user is "{1}".
/// </summary>
internal static string FormatAntiforgeryToken_UsernameMismatch(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_UsernameMismatch"), p0, p1);
}
/// <summary>
/// The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, but the current request is not an SSL request.
/// </summary>
internal static string AntiforgeryWorker_RequireSSL
{
get { return GetString("AntiforgeryWorker_RequireSSL"); }
}
/// <summary>
/// The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, but the current request is not an SSL request.
/// </summary>
internal static string FormatAntiforgeryWorker_RequireSSL()
{
return GetString("AntiforgeryWorker_RequireSSL");
}
/// <summary>
/// Value cannot be null or empty.
/// </summary>
internal static string ArgumentCannotBeNullOrEmpty
{
get { return GetString("ArgumentCannotBeNullOrEmpty"); }
}
/// <summary>
/// Value cannot be null or empty.
/// </summary>
internal static string FormatArgumentCannotBeNullOrEmpty()
{
return GetString("ArgumentCannotBeNullOrEmpty");
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
System.Diagnostics.Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}

Просмотреть файл

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername" xml:space="preserve">
<value>The provided identity of type '{0}' is marked IsAuthenticated = true but does not have a value for Name. By default, the anti-forgery system requires that all authenticated identities have a unique Name. 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.</value>
</data>
<data name="AntiforgeryToken_AdditionalDataCheckFailed" xml:space="preserve">
<value>The provided anti-forgery token failed a custom data check.</value>
</data>
<data name="AntiforgeryToken_ClaimUidMismatch" xml:space="preserve">
<value>The provided anti-forgery token was meant for a different claims-based user than the current user.</value>
</data>
<data name="AntiforgeryToken_CookieMissing" xml:space="preserve">
<value>The required anti-forgery cookie "{0}" is not present.</value>
</data>
<data name="AntiforgeryToken_DeserializationFailed" xml:space="preserve">
<value>The anti-forgery token could not be decrypted.</value>
</data>
<data name="AntiforgeryToken_FormFieldMissing" xml:space="preserve">
<value>The required anti-forgery form field "{0}" is not present.</value>
</data>
<data name="AntiforgeryToken_SecurityTokenMismatch" xml:space="preserve">
<value>The anti-forgery cookie token and form field token do not match.</value>
</data>
<data name="AntiforgeryToken_TokensSwapped" xml:space="preserve">
<value>Validation of the provided anti-forgery token failed. The cookie "{0}" and the form field "{1}" were swapped.</value>
</data>
<data name="AntiforgeryToken_UsernameMismatch" xml:space="preserve">
<value>The provided anti-forgery token was meant for user "{0}", but the current user is "{1}".</value>
</data>
<data name="AntiforgeryWorker_RequireSSL" xml:space="preserve">
<value>The anti-forgery system has the configuration value AntiforgeryOptions.RequireSsl = true, but the current request is not an SSL request.</value>
</data>
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
<value>Value cannot be null or empty.</value>
</data>
</root>

Просмотреть файл

@ -1,22 +1,30 @@
{
"version": "1.0.0-*",
"description": "",
"authors": [ "" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"version": "1.0.0-*",
"description": "",
"authors": [ "" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"dependencies": {
},
"dependencies": {
"Microsoft.AspNet.DataProtection": "1.0.0-*",
"Microsoft.AspNet.Http.Abstractions": "1.0.0-*",
"Microsoft.AspNet.WebUtilities": "1.0.0-*",
"Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-*",
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Framework.OptionsModel": "1.0.0-*"
},
"frameworks" : {
"dnx451": { },
"dnxcore50" : {
"dependencies": {
"System.Collections": "4.0.10-beta-22807",
"System.Linq": "4.0.0-beta-22807",
"System.Threading": "4.0.10-beta-22807",
"Microsoft.CSharp": "4.0.0-beta-22807"
"System.Collections": "4.0.10-beta-*",
"System.Linq": "4.0.0-beta-*",
"System.Security.Cryptography.RandomNumberGenerator": "4.0.0-beta-*",
"System.Security.Cryptography.Hashing.Algorithms": "4.0.0-beta-*",
"System.Threading": "4.0.10-beta-*",
"Microsoft.CSharp": "4.0.0-beta-*"
}
}
}

Просмотреть файл

@ -0,0 +1,184 @@
// 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.Linq;
using Microsoft.AspNet.DataProtection;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Antiforgery
{
public class AntiforgeryTokenSerializerTest
{
private static readonly Mock<IDataProtector> _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;
[Theory]
[InlineData(
"01" // Version
+ "705EEDCC7D42F1D6B3B9" // SecurityToken
// (WRONG!) Stream ends too early
)]
[InlineData(
"01" // Version
+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
+ "01" // IsSessionToken
+ "00" // (WRONG!) Too much data in stream
)]
[InlineData(
"02" // (WRONG! - must be 0x01) Version
+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
+ "01" // IsSessionToken
)]
[InlineData(
"01" // Version
+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
+ "00" // IsSessionToken
+ "00" // IsClaimsBased
+ "05" // Username length header
+ "0000" // (WRONG!) Too little data in stream
)]
public void Deserialize_BadToken_Throws(string serializedToken)
{
// Arrange
var testSerializer = new AntiforgeryTokenSerializer(_dataProtector.Object);
// Act & assert
var ex = Assert.Throws<InvalidOperationException>(() => testSerializer.Deserialize(serializedToken));
Assert.Equal(@"The anti-forgery token could not be decrypted.", ex.Message);
}
[Fact]
public void Serialize_FieldToken_WithClaimUid_TokenRoundTripSuccessful()
{
// Arrange
var testSerializer = new AntiforgeryTokenSerializer(_dataProtector.Object);
//"01" // Version
//+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
//+ "00" // IsSessionToken
//+ "01" // IsClaimsBased
//+ "6F1648E97249AA58754036A67E248CF044F07ECFB0ED387556CE029A4F9A40E0" // ClaimUid
//+ "05" // AdditionalData length header
//+ "E282AC3437"; // AdditionalData ("€47") as UTF8
var token = new AntiforgeryToken()
{
SecurityToken = _securityToken,
IsSessionToken = false,
ClaimUid = _claimUid,
AdditionalData = "€47"
};
// Act
var actualSerializedData = testSerializer.Serialize(token);
var deserializedToken = testSerializer.Deserialize(actualSerializedData);
// Assert
AssertTokensEqual(token, deserializedToken);
_dataProtector.Verify();
}
[Fact]
public void Serialize_FieldToken_WithUsername_TokenRoundTripSuccessful()
{
// Arrange
var testSerializer = new AntiforgeryTokenSerializer(_dataProtector.Object);
//"01" // Version
//+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
//+ "00" // IsSessionToken
//+ "00" // IsClaimsBased
//+ "08" // Username length header
//+ "4AC3A972C3B46D65" // Username ("Jérôme") as UTF8
//+ "05" // AdditionalData length header
//+ "E282AC3437"; // AdditionalData ("€47") as UTF8
var token = new AntiforgeryToken()
{
SecurityToken = _securityToken,
IsSessionToken = false,
Username = "Jérôme",
AdditionalData = "€47"
};
// Act
var actualSerializedData = testSerializer.Serialize(token);
var deserializedToken = testSerializer.Deserialize(actualSerializedData);
// Assert
AssertTokensEqual(token, deserializedToken);
_dataProtector.Verify();
}
[Fact]
public void Serialize_SessionToken_TokenRoundTripSuccessful()
{
// Arrange
var testSerializer = new AntiforgeryTokenSerializer(_dataProtector.Object);
//"01" // Version
//+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
//+ "01"; // IsSessionToken
var token = new AntiforgeryToken()
{
SecurityToken = _securityToken,
IsSessionToken = true
};
// Act
string actualSerializedData = testSerializer.Serialize(token);
var deserializedToken = testSerializer.Deserialize(actualSerializedData);
// Assert
AssertTokensEqual(token, deserializedToken);
_dataProtector.Verify();
}
private static Mock<IDataProtector> GetDataProtector()
{
var mockCryptoSystem = new Mock<IDataProtector>();
mockCryptoSystem.Setup(o => o.Protect(It.IsAny<byte[]>()))
.Returns<byte[]>(Protect)
.Verifiable();
mockCryptoSystem.Setup(o => o.Unprotect(It.IsAny<byte[]>()))
.Returns<byte[]>(UnProtect)
.Verifiable();
return mockCryptoSystem;
}
private static byte[] Protect(byte[] data)
{
var input = new List<byte>(data);
input.Add(_salt);
return input.ToArray();
}
private static byte[] UnProtect(byte[] data)
{
var salt = data[data.Length - 1];
if (salt != _salt)
{
throw new ArgumentException("Invalid salt value in data");
}
return data.Take(data.Length - 1).ToArray();
}
private static void AssertTokensEqual(AntiforgeryToken expected, AntiforgeryToken actual)
{
Assert.NotNull(expected);
Assert.NotNull(actual);
Assert.Equal(expected.AdditionalData, actual.AdditionalData);
Assert.Equal(expected.ClaimUid, actual.ClaimUid);
Assert.Equal(expected.IsSessionToken, actual.IsSessionToken);
Assert.Equal(expected.SecurityToken, actual.SecurityToken);
Assert.Equal(expected.Username, actual.Username);
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,428 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Antiforgery
{
public class AntiforgeryTokenStoreTest
{
private readonly string _cookieName = "cookie-name";
[Fact]
public void GetCookieToken_CookieDoesNotExist_ReturnsNull()
{
// Arrange
var requestCookies = new Mock<IReadableStringCollection>();
requestCookies
.Setup(o => o.Get(It.IsAny<string>()))
.Returns(string.Empty);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext
.Setup(o => o.Request.Cookies)
.Returns(requestCookies.Object);
var contextAccessor = new AntiforgeryContextAccessor();
mockHttpContext.SetupGet(o => o.RequestServices)
.Returns(GetServiceProvider(contextAccessor));
var config = new AntiforgeryOptions()
{
CookieName = _cookieName
};
var tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: null);
// Act
var token = tokenStore.GetCookieToken(mockHttpContext.Object);
// Assert
Assert.Null(token);
}
[Fact]
public void GetCookieToken_CookieIsMissingInRequest_LooksUpCookieInAntiforgeryContext()
{
// Arrange
var requestCookies = new Mock<IReadableStringCollection>();
requestCookies
.Setup(o => o.Get(It.IsAny<string>()))
.Returns(string.Empty);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext
.Setup(o => o.Request.Cookies)
.Returns(requestCookies.Object);
var contextAccessor = new AntiforgeryContextAccessor();
mockHttpContext.SetupGet(o => o.RequestServices)
.Returns(GetServiceProvider(contextAccessor));
// add a cookie explicitly.
var cookie = new AntiforgeryToken();
contextAccessor.Value = new AntiforgeryContext() { CookieToken = cookie };
var config = new AntiforgeryOptions()
{
CookieName = _cookieName
};
var tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: null);
// Act
var token = tokenStore.GetCookieToken(mockHttpContext.Object);
// Assert
Assert.Equal(cookie, token);
}
[Fact]
public void GetCookieToken_CookieIsEmpty_ReturnsNull()
{
// Arrange
var mockHttpContext = GetMockHttpContext(_cookieName, string.Empty);
var config = new AntiforgeryOptions()
{
CookieName = _cookieName
};
var tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: null);
// Act
var token = tokenStore.GetCookieToken(mockHttpContext);
// Assert
Assert.Null(token);
}
[Fact]
public void GetCookieToken_CookieIsInvalid_PropagatesException()
{
// Arrange
var mockHttpContext = GetMockHttpContext(_cookieName, "invalid-value");
var config = new AntiforgeryOptions()
{
CookieName = _cookieName
};
var expectedException = new InvalidOperationException("some exception");
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
mockSerializer
.Setup(o => o.Deserialize("invalid-value"))
.Throws(expectedException);
var tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act & assert
var ex = Assert.Throws<InvalidOperationException>(() => tokenStore.GetCookieToken(mockHttpContext));
Assert.Same(expectedException, ex);
}
[Fact]
public void GetCookieToken_CookieIsValid_ReturnsToken()
{
// Arrange
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 tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act
AntiforgeryToken retVal = tokenStore.GetCookieToken(mockHttpContext);
// Assert
Assert.Same(expectedToken, retVal);
}
[Fact]
public async Task GetFormToken_FormFieldIsEmpty_ReturnsNull()
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
var requestContext = new Mock<HttpRequest>();
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns(string.Empty);
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
mockHttpContext.Setup(o => o.Request)
.Returns(requestContext.Object);
var config = new AntiforgeryOptions()
{
FormFieldName = "form-field-name"
};
var tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: null);
// Act
var token = await tokenStore.GetFormTokenAsync(mockHttpContext.Object);
// Assert
Assert.Null(token);
}
[Fact]
public async Task GetFormToken_FormFieldIsInvalid_PropagatesException()
{
// Arrange
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns("invalid-value");
var requestContext = new Mock<HttpRequest>();
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
var mockHttpContext = new Mock<HttpContext>();
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 tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act & assert
var ex =
await
Assert.ThrowsAsync<InvalidOperationException>(
async () => await tokenStore.GetFormTokenAsync(mockHttpContext.Object));
Assert.Same(expectedException, ex);
}
[Fact]
public async Task GetFormToken_FormFieldIsValid_ReturnsToken()
{
// Arrange
var expectedToken = new AntiforgeryToken();
// Arrange
var mockHttpContext = new Mock<HttpContext>();
var requestContext = new Mock<HttpRequest>();
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns("valid-value");
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
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 tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act
var retVal = await tokenStore.GetFormTokenAsync(mockHttpContext.Object);
// Assert
Assert.Same(expectedToken, retVal);
}
[Theory]
[InlineData(true, true)]
[InlineData(false, null)]
public void SaveCookieToken(bool requireSsl, bool? expectedCookieSecureFlag)
{
// Arrange
var token = new AntiforgeryToken();
var mockCookies = new Mock<IResponseCookies>();
bool defaultCookieSecureValue = expectedCookieSecureFlag ?? false; // pulled from config; set by ctor
var cookies = new MockResponseCookieCollection();
cookies.Count = 0;
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Response.Cookies)
.Returns(cookies);
var contextAccessor = new AntiforgeryContextAccessor();
mockHttpContext.SetupGet(o => o.RequestServices)
.Returns(GetServiceProvider(contextAccessor));
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
mockSerializer.Setup(o => o.Serialize(token))
.Returns("serialized-value");
var config = new AntiforgeryOptions()
{
CookieName = _cookieName,
RequireSSL = requireSsl
};
var tokenStore = new AntiforgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act
tokenStore.SaveCookieToken(mockHttpContext.Object, token);
// Assert
Assert.Equal(1, cookies.Count);
Assert.NotNull(contextAccessor.Value.CookieToken);
Assert.NotNull(cookies);
Assert.Equal(_cookieName, cookies.Key);
Assert.Equal("serialized-value", cookies.Value);
Assert.True(cookies.Options.HttpOnly);
Assert.Equal(defaultCookieSecureValue, cookies.Options.Secure);
}
private HttpContext GetMockHttpContext(string cookieName, string cookieValue)
{
var requestCookies = new MockCookieCollection(new Dictionary<string, string>() { { cookieName, cookieValue } });
var request = new Mock<HttpRequest>();
request.Setup(o => o.Cookies)
.Returns(requestCookies);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Request)
.Returns(request.Object);
var contextAccessor = new AntiforgeryContextAccessor();
mockHttpContext.SetupGet(o => o.RequestServices)
.Returns(GetServiceProvider(contextAccessor));
return mockHttpContext.Object;
}
private static IServiceProvider GetServiceProvider(IAntiforgeryContextAccessor contextAccessor)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddInstance<IAntiforgeryContextAccessor>(contextAccessor);
return serviceCollection.BuildServiceProvider();
}
private class MockResponseCookieCollection : IResponseCookies
{
public string Key { get; set; }
public string Value { get; set; }
public CookieOptions Options { get; set; }
public int Count { get; set; }
public void Append(string key, string value, CookieOptions options)
{
this.Key = key;
this.Value = value;
this.Options = options;
this.Count++;
}
public void Append(string key, string value)
{
throw new NotImplementedException();
}
public void Delete(string key, CookieOptions options)
{
throw new NotImplementedException();
}
public void Delete(string key)
{
throw new NotImplementedException();
}
}
private class MockCookieCollection : IReadableStringCollection
{
private Dictionary<string, string> _dictionary;
public int Count
{
get
{
return _dictionary.Count;
}
}
public ICollection<string> Keys
{
get
{
return _dictionary.Keys;
}
}
public MockCookieCollection(Dictionary<string, string> dictionary)
{
_dictionary = dictionary;
}
public static MockCookieCollection GetDummyInstance(string key, string value)
{
return new MockCookieCollection(new Dictionary<string, string>() { { key, value } });
}
public string Get(string key)
{
return this[key];
}
public IList<string> GetValues(string key)
{
throw new NotImplementedException();
}
public bool ContainsKey(string key)
{
return _dictionary.ContainsKey(key);
}
public string this[string key]
{
get { return _dictionary[key]; }
}
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,132 @@
// 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 Xunit;
namespace Microsoft.AspNet.Antiforgery
{
public class AntiforgeryTokenTest
{
[Fact]
public void AdditionalDataProperty()
{
// Arrange
var token = new AntiforgeryToken();
// Act & assert - 1
Assert.Equal("", token.AdditionalData);
// Act & assert - 2
token.AdditionalData = "additional data";
Assert.Equal("additional data", token.AdditionalData);
// Act & assert - 3
token.AdditionalData = null;
Assert.Equal("", token.AdditionalData);
}
[Fact]
public void ClaimUidProperty()
{
// Arrange
var token = new AntiforgeryToken();
// Act & assert - 1
Assert.Null(token.ClaimUid);
// Act & assert - 2
BinaryBlob blob = new BinaryBlob(32);
token.ClaimUid = blob;
Assert.Equal(blob, token.ClaimUid);
// Act & assert - 3
token.ClaimUid = null;
Assert.Null(token.ClaimUid);
}
[Fact]
public void IsSessionTokenProperty()
{
// Arrange
var token = new AntiforgeryToken();
// Act & assert - 1
Assert.False(token.IsSessionToken);
// Act & assert - 2
token.IsSessionToken = true;
Assert.True(token.IsSessionToken);
// Act & assert - 3
token.IsSessionToken = false;
Assert.False(token.IsSessionToken);
}
[Fact]
public void UsernameProperty()
{
// Arrange
var token = new AntiforgeryToken();
// Act & assert - 1
Assert.Equal("", token.Username);
// Act & assert - 2
token.Username = "my username";
Assert.Equal("my username", token.Username);
// Act & assert - 3
token.Username = null;
Assert.Equal("", token.Username);
}
[Fact]
public void SecurityTokenProperty_GetsAutopopulated()
{
// Arrange
var token = new AntiforgeryToken();
// Act
var securityToken = token.SecurityToken;
// Assert
Assert.NotNull(securityToken);
Assert.Equal(AntiforgeryToken.SecurityTokenBitLength, securityToken.BitLength);
// check that we're not making a new one each property call
Assert.Equal(securityToken, token.SecurityToken);
}
[Fact]
public void SecurityTokenProperty_PropertySetter_DoesNotUseDefaults()
{
// Arrange
var token = new AntiforgeryToken();
// Act
var securityToken = new BinaryBlob(64);
token.SecurityToken = securityToken;
// Assert
Assert.Equal(securityToken, token.SecurityToken);
}
[Fact]
public void SecurityTokenProperty_PropertySetter_DoesNotAllowNulls()
{
// Arrange
var token = new AntiforgeryToken();
// Act
token.SecurityToken = null;
var securityToken = token.SecurityToken;
// Assert
Assert.NotNull(securityToken);
Assert.Equal(AntiforgeryToken.SecurityTokenBitLength, securityToken.BitLength);
// check that we're not making a new one each property call
Assert.Equal(securityToken, token.SecurityToken);
}
}
}

Просмотреть файл

@ -0,0 +1,583 @@
// 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.WebEncoders.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Antiforgery
{
public class AntiforgeryWorkerTest
{
[Fact]
public async Task ChecksSSL_ValidateAsync_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 =
await
Assert.ThrowsAsync<InvalidOperationException>(
async () => await worker.ValidateAsync(mockHttpContext.Object));
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_Validate_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.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));
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_GetTokens_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.GetTokens(mockHttpContext.Object, "cookie-token"));
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 GetFormInputElement_ExistingInvalidCookieToken_GeneratesANewCookieAndAnAntiforgeryToken()
{
// Arrange
var config = new AntiforgeryOptions()
{
FormFieldName = "form-field-name"
};
// Make sure the existing cookie is invalid.
var context = GetAntiforgeryWorkerContext(config, isOldCookieValid: false);
var worker = GetAntiforgeryWorker(context);
// Act
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
// Assert
Assert.Equal(
@"<input name=""HtmlEncode[[form-field-name]]"" type=""HtmlEncode[[hidden]]"" " +
@"value=""HtmlEncode[[serialized-form-token]]"" />",
inputElement);
context.TokenStore.Verify();
}
[Fact]
public void GetFormInputElement_ExistingInvalidCookieToken_SwallowsExceptions()
{
// Arrange
var config = new AntiforgeryOptions()
{
FormFieldName = "form-field-name"
};
// Make sure the existing cookie is invalid.
var context = GetAntiforgeryWorkerContext(config, isOldCookieValid: false);
var worker = GetAntiforgeryWorker(context);
// This will cause the cookieToken to be null.
context.TokenStore.Setup(o => o.GetCookieToken(context.HttpContext.Object))
.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))
.Returns(false);
// Act
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
// Assert
Assert.Equal(
@"<input name=""HtmlEncode[[form-field-name]]"" type=""HtmlEncode[[hidden]]"" " +
@"value=""HtmlEncode[[serialized-form-token]]"" />",
inputElement);
context.TokenStore.Verify();
}
[Fact]
public void GetFormInputElement_ExistingValidCookieToken_GeneratesAnAntiforgeryToken()
{
// Arrange
var options = new AntiforgeryOptions()
{
FormFieldName = "form-field-name"
};
// Make sure the existing cookie is valid and use the same cookie for the mock Token Provider.
var context = GetAntiforgeryWorkerContext(options, useOldCookie: true, isOldCookieValid: true);
var worker = GetAntiforgeryWorker(context);
// Act
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
// Assert
Assert.Equal(
@"<input name=""HtmlEncode[[form-field-name]]"" type=""HtmlEncode[[hidden]]"" " +
@"value=""HtmlEncode[[serialized-form-token]]"" />",
inputElement);
}
[Theory]
[InlineData(false, "SAMEORIGIN")]
[InlineData(true, null)]
public void GetFormInputElement_AddsXFrameOptionsHeader(bool suppressXFrameOptions, string expectedHeaderValue)
{
// Arrange
var options = new AntiforgeryOptions()
{
SuppressXFrameOptionsHeader = suppressXFrameOptions
};
// Genreate a new cookie.
var context = GetAntiforgeryWorkerContext(options, useOldCookie: false, isOldCookieValid: false);
var worker = GetAntiforgeryWorker(context);
// Act
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
// Assert
string xFrameOptions = context.HttpContext.Object.Response.Headers["X-Frame-Options"];
Assert.Equal(expectedHeaderValue, xFrameOptions);
}
[Fact]
public void GetTokens_ExistingInvalidCookieToken_GeneratesANewCookieTokenAndANewFormToken()
{
// Arrange
// Genreate a new cookie.
var context = GetAntiforgeryWorkerContext(
new AntiforgeryOptions(),
useOldCookie: false,
isOldCookieValid: false);
var worker = GetAntiforgeryWorker(context);
// Act
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
// Assert
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.FormToken);
}
[Fact]
public void GetTokens_ExistingInvalidCookieToken_SwallowsExceptions()
{
// Arrange
// Make sure the existing cookie is invalid.
var context = GetAntiforgeryWorkerContext(
new AntiforgeryOptions(),
useOldCookie: false,
isOldCookieValid: false);
// This will cause the cookieToken to be null.
context.TokenSerializer.Setup(o => o.Deserialize("serialized-old-cookie-token"))
.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))
.Returns(false);
var worker = GetAntiforgeryWorker(context);
// Act
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
// Assert
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.FormToken);
}
[Fact]
public void GetTokens_ExistingValidCookieToken_GeneratesANewFormToken()
{
// Arrange
var context = GetAntiforgeryWorkerContext(
new AntiforgeryOptions(),
useOldCookie: true,
isOldCookieValid: true);
context.TokenStore = null;
var worker = GetAntiforgeryWorker(context);
// Act
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
// Assert
Assert.Null(tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.FormToken);
}
[Fact]
public void Validate_FromInvalidStrings_Throws()
{
// Arrange
var context = GetAntiforgeryWorkerContext(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.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Throws(new InvalidOperationException("my-message"));
context.TokenStore = null;
var worker = GetAntiforgeryWorker(context);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => worker.Validate(context.HttpContext.Object, "cookie-token", "form-token"));
Assert.Equal("my-message", ex.Message);
}
[Fact]
public void Validate_FromValidStrings_TokensValidatedSuccessfully()
{
// Arrange
var context = GetAntiforgeryWorkerContext(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.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Verifiable();
context.TokenStore = null;
var worker = GetAntiforgeryWorker(context);
// Act
worker.Validate(context.HttpContext.Object, "cookie-token", "form-token");
// Assert
context.TokenValidator.Verify();
}
[Fact]
public async Task Validate_FromStore_Failure()
{
// Arrange
var context = GetAntiforgeryWorkerContext(new AntiforgeryOptions());
context.TokenValidator.Setup(o => o.ValidateTokens(
context.HttpContext.Object,
context.HttpContext.Object.User.Identity as ClaimsIdentity,
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Throws(new InvalidOperationException("my-message"));
context.TokenSerializer = null;
var worker = GetAntiforgeryWorker(context);
// Act & assert
var ex =
await
Assert.ThrowsAsync<InvalidOperationException>(
async () => await worker.ValidateAsync(context.HttpContext.Object));
Assert.Equal("my-message", ex.Message);
}
[Fact]
public async Task Validate_FromStore_Success()
{
// Arrange
var context = GetAntiforgeryWorkerContext(new AntiforgeryOptions());
context.TokenValidator.Setup(o => o.ValidateTokens(
context.HttpContext.Object,
context.HttpContext.Object.User.Identity as ClaimsIdentity,
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Verifiable();
context.TokenSerializer = null;
var worker = GetAntiforgeryWorker(context);
// Act
await worker.ValidateAsync(context.HttpContext.Object);
// Assert
context.TokenValidator.Verify();
}
[Theory]
[InlineData(false, "SAMEORIGIN")]
[InlineData(true, null)]
public void SetCookieTokenAndHeader_AddsXFrameOptionsHeader(
bool suppressXFrameOptions,
string expectedHeaderValue)
{
// Arrange
var options = new AntiforgeryOptions()
{
SuppressXFrameOptionsHeader = suppressXFrameOptions
};
// Genreate a new cookie.
var context = GetAntiforgeryWorkerContext(options, useOldCookie: false, isOldCookieValid: false);
var worker = GetAntiforgeryWorker(context);
// Act
worker.SetCookieTokenAndHeader(context.HttpContext.Object);
// Assert
var xFrameOptions = context.HttpContext.Object.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());
}
private Mock<HttpContext> GetHttpContext(bool setupResponse = true)
{
var identity = new ClaimsIdentity("some-auth");
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.User)
.Returns(new ClaimsPrincipal(identity));
if (setupResponse)
{
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);
}
return mockHttpContext;
}
private Mock<IAntiforgeryTokenStore> GetTokenStore(
HttpContext context,
TestTokenSet testTokenSet,
bool saveNewCookie = true)
{
var oldCookieToken = testTokenSet.OldCookieToken;
var formToken = testTokenSet.FormToken;
var mockTokenStore = new Mock<IAntiforgeryTokenStore>(MockBehavior.Strict);
mockTokenStore.Setup(o => o.GetCookieToken(context))
.Returns(oldCookieToken);
mockTokenStore.Setup(o => o.GetFormTokenAsync(context))
.Returns(Task.FromResult(formToken));
if (saveNewCookie)
{
var newCookieToken = testTokenSet.NewCookieToken;
mockTokenStore.Setup(o => o.SaveCookieToken(context, newCookieToken))
.Verifiable();
}
return mockTokenStore;
}
private Mock<IAntiforgeryTokenSerializer> GetTokenSerializer(TestTokenSet testTokenSet)
{
var oldCookieToken = testTokenSet.OldCookieToken;
var newCookieToken = testTokenSet.NewCookieToken;
var formToken = testTokenSet.FormToken;
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>(MockBehavior.Strict);
mockSerializer.Setup(o => o.Serialize(formToken))
.Returns("serialized-form-token");
mockSerializer.Setup(o => o.Deserialize("serialized-old-cookie-token"))
.Returns(oldCookieToken);
mockSerializer.Setup(o => o.Serialize(newCookieToken))
.Returns("serialized-new-cookie-token");
return mockSerializer;
}
private TestTokenSet GetTokenSet(bool isOldCookieTokenSessionToken = true, bool isNewCookieSessionToken = true)
{
return new TestTokenSet()
{
FormToken = new AntiforgeryToken() { IsSessionToken = false },
OldCookieToken = new AntiforgeryToken() { IsSessionToken = isOldCookieTokenSessionToken },
NewCookieToken = new AntiforgeryToken() { IsSessionToken = isNewCookieSessionToken },
};
}
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
{
public AntiforgeryOptions Options { get; set; }
public TestTokenSet TestTokenSet { get; set; }
public Mock<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

Просмотреть файл

@ -0,0 +1,129 @@
// 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 Xunit;
namespace Microsoft.AspNet.Antiforgery
{
public class BinaryBlobTest
{
[Fact]
public void Ctor_BitLength()
{
// Act
var blob = new BinaryBlob(bitLength: 64);
var data = blob.GetData();
// Assert
Assert.Equal(64, blob.BitLength);
Assert.Equal(64 / 8, data.Length);
Assert.NotEqual(new byte[64 / 8], data); // should not be a zero-filled array
}
[Theory]
[InlineData(24)]
[InlineData(33)]
public void Ctor_BitLength_Bad(int bitLength)
{
// Act & assert
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new BinaryBlob(bitLength));
Assert.Equal("bitLength", ex.ParamName);
}
[Fact]
public void Ctor_BitLength_ProducesDifferentValues()
{
// Act
var blobA = new BinaryBlob(bitLength: 64);
var blobB = new BinaryBlob(bitLength: 64);
// Assert
Assert.NotEqual(blobA.GetData(), blobB.GetData());
}
[Fact]
public void Ctor_Data()
{
// Arrange
var expectedData = new byte[] { 0x01, 0x02, 0x03, 0x04 };
// Act
var blob = new BinaryBlob(32, expectedData);
// Assert
Assert.Equal(32, blob.BitLength);
Assert.Equal(expectedData, blob.GetData());
}
[Theory]
[InlineData((object[])null)]
[InlineData(new byte[] { 0x01, 0x02, 0x03 })]
public void Ctor_Data_Bad(byte[] data)
{
// Act & assert
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new BinaryBlob(32, data));
Assert.Equal("data", ex.ParamName);
}
[Fact]
public void Equals_DifferentData_ReturnsFalse()
{
// Arrange
object blobA = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 });
object blobB = new BinaryBlob(32, new byte[] { 0x04, 0x03, 0x02, 0x01 });
// Act & assert
Assert.NotEqual(blobA, blobB);
}
[Fact]
public void Equals_NotABlob_ReturnsFalse()
{
// Arrange
object blobA = new BinaryBlob(32);
object blobB = "hello";
// Act & assert
Assert.NotEqual(blobA, blobB);
}
[Fact]
public void Equals_Null_ReturnsFalse()
{
// Arrange
object blobA = new BinaryBlob(32);
object blobB = null;
// Act & assert
Assert.NotEqual(blobA, blobB);
}
[Fact]
public void Equals_SameData_ReturnsTrue()
{
// Arrange
object blobA = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 });
object blobB = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 });
// Act & assert
Assert.Equal(blobA, blobB);
}
[Fact]
public void GetHashCodeTest()
{
// Arrange
var blobData = new byte[] { 0x01, 0x02, 0x03, 0x04 };
var expectedHashCode = BitConverter.ToInt32(blobData, 0);
var blob = new BinaryBlob(32, blobData);
// Act
var actualHashCode = blob.GetHashCode();
// Assert
Assert.Equal(expectedHashCode, actualHashCode);
}
}
}

Просмотреть файл

@ -0,0 +1,115 @@
// 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.Linq;
using System.Security.Claims;
#if DNX451
using Moq;
#endif
using Xunit;
namespace Microsoft.AspNet.Antiforgery
{
public class ClaimUidExtractorTest
{
[Fact]
public void ExtractClaimUid_NullIdentity()
{
// Arrange
IClaimUidExtractor extractor = new DefaultClaimUidExtractor();
// Act
var claimUid = extractor.ExtractClaimUid(null);
// Assert
Assert.Null(claimUid);
}
#if DNX451
[Fact]
public void ExtractClaimUid_Unauthenticated()
{
// Arrange
IClaimUidExtractor extractor = new DefaultClaimUidExtractor();
var mockIdentity = new Mock<ClaimsIdentity>();
mockIdentity.Setup(o => o.IsAuthenticated)
.Returns(false);
// Act
var claimUid = extractor.ExtractClaimUid(mockIdentity.Object);
// Assert
Assert.Null(claimUid);
}
[Fact]
public void ExtractClaimUid_ClaimsIdentity()
{
// Arrange
var mockIdentity = new Mock<ClaimsIdentity>();
mockIdentity.Setup(o => o.IsAuthenticated)
.Returns(true);
IClaimUidExtractor extractor = new DefaultClaimUidExtractor();
// Act
var claimUid = extractor.ExtractClaimUid(mockIdentity.Object);
// Assert
Assert.NotNull(claimUid);
Assert.Equal("47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=", claimUid);
}
#endif
[Fact]
public void DefaultUniqueClaimTypes_NotPresent_SerializesAllClaimTypes()
{
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.Email, "someone@antifrogery.com"));
identity.AddClaim(new Claim(ClaimTypes.GivenName, "some"));
identity.AddClaim(new Claim(ClaimTypes.Surname, "one"));
#if DNX451
// CoreCLR doesn't support an 'empty' name
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, string.Empty));
#endif
// Arrange
var claimsIdentity = (ClaimsIdentity)identity;
// Act
var identiferParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(claimsIdentity)
.ToArray();
var claims = claimsIdentity.Claims.ToList();
claims.Sort((a, b) => string.Compare(a.Type, b.Type, StringComparison.Ordinal));
// Assert
int index = 0;
foreach (var claim in claims)
{
Assert.Equal(identiferParameters[index++], claim.Type);
Assert.Equal(identiferParameters[index++], claim.Value);
}
}
[Fact]
public void DefaultUniqueClaimTypes_Present()
{
// Arrange
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim("fooClaim", "fooClaimValue"));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "nameIdentifierValue"));
// Act
var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(identity);
// Assert
Assert.Equal(new string[]
{
ClaimTypes.NameIdentifier,
"nameIdentifierValue",
}, uniqueIdentifierParameters);
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>415e83f8-6002-47e4-aa8e-cd5169c06f28</ProjectGuid>
@ -12,9 +11,11 @@
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

Просмотреть файл

@ -0,0 +1,588 @@
// 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.Security.Claims;
using System.Security.Cryptography;
using Microsoft.AspNet.Http;
#if DNX451
using Moq;
#endif
using Xunit;
namespace Microsoft.AspNet.Antiforgery
{
public class TokenProviderTest
{
[Fact]
public void GenerateCookieToken()
{
// Arrange
var tokenProvider = new AntiforgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
var retVal = tokenProvider.GenerateCookieToken();
// Assert
Assert.NotNull(retVal);
}
#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 config = new AntiforgeryOptions();
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
var fieldToken = tokenProvider.GenerateFormToken(httpContext, mockIdentity.Object, cookieToken);
// Assert
Assert.NotNull(fieldToken);
Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken);
Assert.False(fieldToken.IsSessionToken);
Assert.Empty(fieldToken.Username);
Assert.Null(fieldToken.ClaimUid);
Assert.Empty(fieldToken.AdditionalData);
}
[Fact]
public void GenerateFormToken_AuthenticatedWithoutUsernameAndNoAdditionalData_NoAdditionalData()
{
// Arrange
var cookieToken = new AntiforgeryToken()
{
IsSessionToken = true
};
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new MyAuthenticatedIdentityWithoutUsername();
var config = new AntiforgeryOptions();
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: claimUidExtractor,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.GenerateFormToken(httpContext, identity, cookieToken));
Assert.Equal(
"The provided identity of type " +
$"'{typeof(MyAuthenticatedIdentityWithoutUsername).FullName}' " +
"is marked IsAuthenticated = true but does not have a value for Name. " +
"By default, the anti-forgery system requires that all authenticated identities have a unique Name. " +
"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);
}
[Fact]
public void GenerateFormToken_AuthenticatedWithoutUsername_WithAdditionalData()
{
// Arrange
var cookieToken = new AntiforgeryToken() { IsSessionToken = true };
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = 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 tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: claimUidExtractor,
additionalDataProvider: mockAdditionalDataProvider.Object);
// Act
var fieldToken = tokenProvider.GenerateFormToken(httpContext, identity, cookieToken);
// Assert
Assert.NotNull(fieldToken);
Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken);
Assert.False(fieldToken.IsSessionToken);
Assert.Empty(fieldToken.Username);
Assert.Null(fieldToken.ClaimUid);
Assert.Equal("additional-data", fieldToken.AdditionalData);
}
[Fact]
public void GenerateFormToken_ClaimsBasedIdentity()
{
// Arrange
var cookieToken = new AntiforgeryToken() { IsSessionToken = true };
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity("some-identity");
var config = new AntiforgeryOptions();
byte[] data = new byte[256 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(data);
}
var base64ClaimUId = Convert.ToBase64String(data);
var expectedClaimUid = new BinaryBlob(256, data);
var mockClaimUidExtractor = new Mock<IClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
.Returns(base64ClaimUId);
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
// Act
var fieldToken = tokenProvider.GenerateFormToken(httpContext, identity, cookieToken);
// Assert
Assert.NotNull(fieldToken);
Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken);
Assert.False(fieldToken.IsSessionToken);
Assert.Equal("", fieldToken.Username);
Assert.Equal(expectedClaimUid, fieldToken.ClaimUid);
Assert.Equal("", fieldToken.AdditionalData);
}
[Fact]
public void GenerateFormToken_RegularUserWithUsername()
{
// Arrange
var cookieToken = new AntiforgeryToken() { IsSessionToken = true };
var httpContext = new Mock<HttpContext>().Object;
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;
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: claimUidExtractor,
additionalDataProvider: null);
// Act
var fieldToken = tokenProvider.GenerateFormToken(httpContext, mockIdentity.Object, cookieToken);
// Assert
Assert.NotNull(fieldToken);
Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken);
Assert.False(fieldToken.IsSessionToken);
Assert.Equal("my-username", fieldToken.Username);
Assert.Null(fieldToken.ClaimUid);
Assert.Empty(fieldToken.AdditionalData);
}
#endif
[Fact]
public void IsCookieTokenValid_FieldToken_ReturnsFalse()
{
// Arrange
var cookieToken = new AntiforgeryToken()
{
IsSessionToken = false
};
var tokenProvider = new AntiforgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
// Assert
Assert.False(retVal);
}
[Fact]
public void IsCookieTokenValid_NullToken_ReturnsFalse()
{
// Arrange
AntiforgeryToken cookieToken = null;
var tokenProvider = new AntiforgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
// Assert
Assert.False(retVal);
}
[Fact]
public void IsCookieTokenValid_ValidToken_ReturnsTrue()
{
// Arrange
var cookieToken = new AntiforgeryToken()
{
IsSessionToken = true
};
var tokenProvider = new AntiforgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
// Assert
Assert.True(retVal);
}
#if DNX451
[Fact]
public void ValidateTokens_SessionTokenMissing()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
AntiforgeryToken sessionToken = null;
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
var config = new AntiforgeryOptions()
{
CookieName = "my-cookie-name"
};
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(@"The required anti-forgery cookie ""my-cookie-name"" is not present.", ex.Message);
}
[Fact]
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 config = new AntiforgeryOptions()
{
FormFieldName = "my-form-field-name"
};
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(@"The required anti-forgery form field ""my-form-field-name"" is not present.", ex.Message);
}
[Fact]
public void ValidateTokens_FieldAndSessionTokensSwapped()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
var config = new AntiforgeryOptions()
{
CookieName = "my-cookie-name",
FormFieldName = "my-form-field-name"
};
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex1 =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, 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.",
ex1.Message);
var ex2 =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, 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.",
ex2.Message);
}
[Fact]
public void ValidateTokens_FieldAndSessionTokensHaveDifferentSecurityKeys()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
var tokenProvider = new AntiforgeryTokenProvider(
config: null,
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);
}
[Theory]
[InlineData("the-user", "the-other-user")]
[InlineData("http://example.com/uri-casing", "http://example.com/URI-casing")]
[InlineData("https://example.com/secure-uri-casing", "https://example.com/secure-URI-casing")]
public void ValidateTokens_UsernameMismatch(string identityUsername, string embeddedUsername)
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity(identityUsername);
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
Username = embeddedUsername,
IsSessionToken = false
};
var mockClaimUidExtractor = new Mock<IClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
.Returns((string)null);
var tokenProvider = new AntiforgeryTokenProvider(
config: null,
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(
@"The provided anti-forgery token was meant for user """ + embeddedUsername +
@""", but the current user is """ + identityUsername + @""".", ex.Message);
}
[Fact]
public void ValidateTokens_ClaimUidMismatch()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity("the-user");
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
IsSessionToken = false,
ClaimUid = new BinaryBlob(256)
};
var differentToken = new BinaryBlob(256);
var mockClaimUidExtractor = new Mock<IClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
.Returns(Convert.ToBase64String(differentToken.GetData()));
var tokenProvider = new AntiforgeryTokenProvider(
config: null,
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(
@"The provided anti-forgery token was meant for a different claims-based user than the current user.",
ex.Message);
}
[Fact]
public void ValidateTokens_AdditionalDataRejected()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = new ClaimsIdentity();
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
Username = String.Empty,
IsSessionToken = false,
AdditionalData = "some-additional-data"
};
var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
.Returns(false);
var config = new AntiforgeryOptions();
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
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);
}
[Fact]
public void ValidateTokens_Success_AnonymousUser()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = new ClaimsIdentity();
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
Username = String.Empty,
IsSessionToken = false,
AdditionalData = "some-additional-data"
};
var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
.Returns(true);
var config = new AntiforgeryOptions();
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: mockAdditionalDataProvider.Object);
// Act
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
[Fact]
public void ValidateTokens_Success_AuthenticatedUserWithUsername()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity("the-user");
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
Username = "THE-USER",
IsSessionToken = false,
AdditionalData = "some-additional-data"
};
var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
.Returns(true);
var config = new AntiforgeryOptions();
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: new Mock<IClaimUidExtractor>().Object,
additionalDataProvider: mockAdditionalDataProvider.Object);
// Act
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
[Fact]
public void ValidateTokens_Success_ClaimsBasedUser()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity("the-user");
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
IsSessionToken = false,
ClaimUid = new BinaryBlob(256)
};
var mockClaimUidExtractor = new Mock<IClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
.Returns(Convert.ToBase64String(fieldtoken.ClaimUid.GetData()));
var config = new AntiforgeryOptions();
var tokenProvider = new AntiforgeryTokenProvider(
config: config,
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
// Act
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
#endif
private static ClaimsIdentity GetAuthenticatedIdentity(string identityUsername)
{
var claim = new Claim(ClaimsIdentity.DefaultNameClaimType, identityUsername);
return new ClaimsIdentity(new[] { claim }, "Some-Authentication");
}
private sealed class MyAuthenticatedIdentityWithoutUsername : ClaimsIdentity
{
public override bool IsAuthenticated
{
get { return true; }
}
public override string Name
{
get { return String.Empty; }
}
}
}
}

Просмотреть файл

@ -1,6 +1,9 @@
{
"dependencies": {
"Microsoft.AspNet.Antiforgery": "1.0.0-*",
"Microsoft.AspNet.Http": "1.0.0-*",
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
"Microsoft.Framework.WebEncoders.Testing": "1.0.0-*",
"xunit.runner.aspnet": "2.0.0-aspnet-*"
},
"commands": {
@ -8,7 +11,11 @@
"test": "xunit.runner.aspnet"
},
"frameworks": {
"dnx451": { },
"dnx451": {
"dependencies": {
"Moq": "4.2.1312.1622"
}
},
"dnxcore50": { }
}
}