Tests for AntiForgery System.
This commit is contained in:
parent
b444f66689
commit
bbafa0a29a
|
|
@ -30,6 +30,9 @@ namespace Microsoft.AspNet.Mvc
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
return new AntiForgeryTokenSetInternal()
|
||||
{
|
||||
// Note : The new cookie would be null if the old cookie is valid.
|
||||
CookieToken = newCookieToken,
|
||||
FormToken = formToken
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
return Convert.ToBase64String(claimUidBytes);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetUniqueIdentifierParameters(ClaimsIdentity claimsIdentity)
|
||||
internal static IEnumerable<string> GetUniqueIdentifierParameters(ClaimsIdentity claimsIdentity)
|
||||
{
|
||||
// TODO: Need to enable support for special casing acs identities.
|
||||
var nameIdentifierClaim = claimsIdentity.FindFirst(claim =>
|
||||
|
|
|
|||
|
|
@ -52,3 +52,4 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.Mvc.Core.Test")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.
|
||||
/// The anti-forgery token could not be decrypted.
|
||||
/// </summary>
|
||||
internal static string AntiForgeryToken_DeserializationFailed
|
||||
{
|
||||
|
|
@ -84,7 +84,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.
|
||||
/// The anti-forgery token could not be decrypted.
|
||||
/// </summary>
|
||||
internal static string FormatAntiForgeryToken_DeserializationFailed()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@
|
|||
<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. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.</value>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,198 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Microsoft.AspNet.Security.DataProtection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,366 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
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 config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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_CookieIsEmpty_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var mockHttpContext = GetMockHttpContext(_cookieName, string.Empty);
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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 MockAntiForgeryConfig()
|
||||
{
|
||||
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");
|
||||
|
||||
MockAntiForgeryConfig config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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>();
|
||||
IReadableStringCollection formsCollection =
|
||||
new MockCookieCollection(new Dictionary<string, string>() { { "form-field-name", string.Empty } });
|
||||
requestContext.Setup(o => o.GetFormAsync())
|
||||
.Returns(Task.FromResult(formsCollection));
|
||||
mockHttpContext.Setup(o => o.Request)
|
||||
.Returns(requestContext.Object);
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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
|
||||
IReadableStringCollection formsCollection =
|
||||
new MockCookieCollection(new Dictionary<string, string>() { { "form-field-name", "invalid-value" } });
|
||||
|
||||
var requestContext = new Mock<HttpRequest>();
|
||||
requestContext.Setup(o => o.GetFormAsync())
|
||||
.Returns(Task.FromResult(formsCollection));
|
||||
|
||||
var mockHttpContext = new Mock<HttpContext>();
|
||||
mockHttpContext.Setup(o => o.Request)
|
||||
.Returns(requestContext.Object);
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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>();
|
||||
IReadableStringCollection formsCollection =
|
||||
new MockCookieCollection(new Dictionary<string, string>() { { "form-field-name", "valid-value" } });
|
||||
requestContext.Setup(o => o.GetFormAsync())
|
||||
.Returns(Task.FromResult(formsCollection));
|
||||
mockHttpContext.Setup(o => o.Request)
|
||||
.Returns(requestContext.Object);
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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>();
|
||||
|
||||
// TODO : Once we decide on where to pick this value from enable this.
|
||||
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 mockSerializer = new Mock<IAntiForgeryTokenSerializer>();
|
||||
mockSerializer.Setup(o => o.Serialize(token))
|
||||
.Returns("serialized-value");
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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(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);
|
||||
|
||||
return mockHttpContext.Object;
|
||||
}
|
||||
|
||||
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 MockCookieCollection(Dictionary<string, string> dictionary)
|
||||
{
|
||||
this.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 string this[string key]
|
||||
{
|
||||
get { return this.dictionary[key]; }
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
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,539 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.PipelineCore.Collections;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
public class AntiForgeryWorkerTest
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public async Task ChecksSSL_ValidateAsync_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var mockHttpContext = new Mock<HttpContext>();
|
||||
mockHttpContext.Setup(o => o.Request.IsSecure)
|
||||
.Returns(false);
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var worker = new AntiForgeryWorker(
|
||||
config: config,
|
||||
serializer: null,
|
||||
tokenStore: null,
|
||||
generator: null,
|
||||
validator: null);
|
||||
|
||||
// Act & assert
|
||||
var ex =
|
||||
await
|
||||
Assert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await worker.ValidateAsync(mockHttpContext.Object));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery system has the configuration value AntiForgeryConfig.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.IsSecure)
|
||||
.Returns(false);
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var worker = new AntiForgeryWorker(
|
||||
config: config,
|
||||
serializer: null,
|
||||
tokenStore: null,
|
||||
generator: null,
|
||||
validator: null);
|
||||
|
||||
// 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 AntiForgeryConfig.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.IsSecure)
|
||||
.Returns(false);
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var worker = new AntiForgeryWorker(
|
||||
config: config,
|
||||
serializer: null,
|
||||
tokenStore: null,
|
||||
generator: null,
|
||||
validator: null);
|
||||
|
||||
// Act & assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => worker.GetFormInputElement(mockHttpContext.Object));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery system has the configuration value AntiForgeryConfig.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.IsSecure)
|
||||
.Returns(false);
|
||||
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
RequireSSL = true
|
||||
};
|
||||
|
||||
var worker = new AntiForgeryWorker(
|
||||
config: config,
|
||||
serializer: null,
|
||||
tokenStore: null,
|
||||
generator: null,
|
||||
validator: null);
|
||||
|
||||
// Act & assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => worker.GetTokens(mockHttpContext.Object, "cookie-token"));
|
||||
Assert.Equal(
|
||||
@"The anti-forgery system has the configuration value AntiForgeryConfig.RequireSsl = true, " +
|
||||
"but the current request is not an SSL request.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFormInputElement_ExistingInvalidCookieToken_GeneratesANewCookieAndAnAntiForgeryToken()
|
||||
{
|
||||
// Arrange
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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=""form-field-name"" type=""hidden"" value=""serialized-form-token"" />",
|
||||
inputElement.ToString(TagRenderMode.SelfClosing));
|
||||
context.TokenStore.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFormInputElement_ExistingInvalidCookieToken_SwallowsExceptions()
|
||||
{
|
||||
// Arrange
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
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.TokenProvider.Setup(o => o.IsCookieTokenValid(null))
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(@"<input name=""form-field-name"" type=""hidden"" value=""serialized-form-token"" />",
|
||||
inputElement.ToString(TagRenderMode.SelfClosing));
|
||||
context.TokenStore.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFormInputElement_ExistingValidCookieToken_GeneratesAnAntiForgeryToken()
|
||||
{
|
||||
// Arrange
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
FormFieldName = "form-field-name"
|
||||
};
|
||||
|
||||
// Make sure the existing cookie is valid and use the same cookie for the mock Token Provider.
|
||||
var context = GetAntiForgeryWorkerContext(config, useOldCookie: true, isOldCookieValid: true);
|
||||
var worker = GetAntiForgeryWorker(context);
|
||||
|
||||
// Act
|
||||
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(@"<input name=""form-field-name"" type=""hidden"" value=""serialized-form-token"" />",
|
||||
inputElement.ToString(TagRenderMode.SelfClosing));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, "SAMEORIGIN")]
|
||||
[InlineData(true, null)]
|
||||
public void GetFormInputElement_AddsXFrameOptionsHeader(bool suppressXFrameOptions, string expectedHeaderValue)
|
||||
{
|
||||
// Arrange
|
||||
var config = new MockAntiForgeryConfig()
|
||||
{
|
||||
SuppressXFrameOptionsHeader = suppressXFrameOptions
|
||||
};
|
||||
|
||||
// Genreate a new cookie.
|
||||
var context = GetAntiForgeryWorkerContext(config, 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 MockAntiForgeryConfig(), 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 MockAntiForgeryConfig(), 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.TokenProvider.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 MockAntiForgeryConfig(), 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 MockAntiForgeryConfig());
|
||||
|
||||
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.TokenProvider.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 MockAntiForgeryConfig());
|
||||
|
||||
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.TokenProvider.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.TokenProvider.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Validate_FromStore_Failure()
|
||||
{
|
||||
// Arrange
|
||||
var context = GetAntiForgeryWorkerContext(new MockAntiForgeryConfig());
|
||||
|
||||
context.TokenProvider.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 MockAntiForgeryConfig());
|
||||
|
||||
context.TokenProvider.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.TokenProvider.Verify();
|
||||
}
|
||||
|
||||
private AntiForgeryWorker GetAntiForgeryWorker(AntiForgeryWorkerContext context)
|
||||
{
|
||||
return new AntiForgeryWorker(
|
||||
config: context.Config,
|
||||
serializer: context.TokenSerializer != null ? context.TokenSerializer.Object : null,
|
||||
tokenStore: context.TokenStore != null ? context.TokenStore.Object : null,
|
||||
generator: context.TokenProvider != null ? context.TokenProvider.Object : null,
|
||||
validator: context.TokenProvider != null ? context.TokenProvider.Object : null);
|
||||
}
|
||||
|
||||
private Mock<HttpContext> GetHttpContext(bool setupResponse = true)
|
||||
{
|
||||
var identity = new GenericIdentity("some-user");
|
||||
var mockHttpContext = new Mock<HttpContext>();
|
||||
|
||||
mockHttpContext.Setup(o => o.User)
|
||||
.Returns(new GenericPrincipal(identity, new string[0]));
|
||||
|
||||
|
||||
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<ITokenProvider> GetTokenProvider(HttpContext context, TestTokenSet testTokenSet, bool useOldCookie, bool isOldCookieValid = true, bool isNewCookieValid = true)
|
||||
{
|
||||
var oldCookieToken = testTokenSet.OldCookieToken;
|
||||
var newCookieToken = testTokenSet.NewCookieToken;
|
||||
var formToken = testTokenSet.FormToken;
|
||||
var mockValidator = new Mock<ITokenProvider>(MockBehavior.Strict);
|
||||
mockValidator.Setup(o => o.GenerateFormToken(context, context.User.Identity as ClaimsIdentity, useOldCookie ? oldCookieToken : newCookieToken))
|
||||
.Returns(formToken);
|
||||
mockValidator.Setup(o => o.IsCookieTokenValid(oldCookieToken))
|
||||
.Returns(isOldCookieValid);
|
||||
mockValidator.Setup(o => o.IsCookieTokenValid(newCookieToken))
|
||||
.Returns(isNewCookieValid);
|
||||
|
||||
mockValidator.Setup(o => o.GenerateCookieToken())
|
||||
.Returns(useOldCookie ? oldCookieToken : newCookieToken);
|
||||
|
||||
return mockValidator;
|
||||
}
|
||||
|
||||
private Mock<ITokenStore> GetTokenStore(HttpContext context, TestTokenSet testTokenSet, bool saveNewCookie = true)
|
||||
{
|
||||
var oldCookieToken = testTokenSet.OldCookieToken;
|
||||
var formToken = testTokenSet.FormToken;
|
||||
var mockTokenStore = new Mock<ITokenStore>(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(MockAntiForgeryConfig 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 mockTokenProvider = GetTokenProvider(mockHttpContext.Object, testTokenSet, useOldCookie: useOldCookie, isOldCookieValid: isOldCookieValid);
|
||||
|
||||
return new AntiForgeryWorkerContext()
|
||||
{
|
||||
Config = config,
|
||||
HttpContext = mockHttpContext,
|
||||
TokenProvider = mockTokenProvider,
|
||||
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 MockAntiForgeryConfig Config { get; set; }
|
||||
|
||||
public TestTokenSet TestTokenSet { get; set; }
|
||||
|
||||
public Mock<HttpContext> HttpContext { get; set; }
|
||||
|
||||
public Mock<ITokenProvider> TokenProvider { get; set; }
|
||||
|
||||
public Mock<ITokenStore> TokenStore { get; set; }
|
||||
|
||||
public Mock<IAntiForgeryTokenSerializer> TokenSerializer { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
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,122 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
public class ClaimUidExtractorTest
|
||||
{
|
||||
[Fact]
|
||||
public void ExtractClaimUid_NullIdentity()
|
||||
{
|
||||
// Arrange
|
||||
IClaimUidExtractor extractor = new DefaultClaimUidExtractor();
|
||||
|
||||
// Act
|
||||
var claimUid = extractor.ExtractClaimUid(null);
|
||||
|
||||
// Assert
|
||||
Assert.Null(claimUid);
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultUniqueClaimTypes_NotPresent_SerializesAllClaimTypes()
|
||||
{
|
||||
var identity = new MockClaimsIdentity();
|
||||
identity.AddClaim(ClaimTypes.Email, "someone@antifrogery.com");
|
||||
identity.AddClaim(ClaimTypes.GivenName, "some");
|
||||
identity.AddClaim(ClaimTypes.Surname, "one");
|
||||
identity.AddClaim(ClaimTypes.NameIdentifier, String.Empty);
|
||||
|
||||
// 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.True(String.Equals(identiferParameters[index++], claim.Type, StringComparison.Ordinal));
|
||||
Assert.True(String.Equals(identiferParameters[index++], claim.Value, StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultUniqueClaimTypes_Present()
|
||||
{
|
||||
// Arrange
|
||||
var identity = new MockClaimsIdentity();
|
||||
identity.AddClaim("fooClaim", "fooClaimValue");
|
||||
identity.AddClaim(ClaimTypes.NameIdentifier, "nameIdentifierValue");
|
||||
|
||||
// Act
|
||||
var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(identity);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new string[]
|
||||
{
|
||||
ClaimTypes.NameIdentifier,
|
||||
"nameIdentifierValue",
|
||||
}, uniqueIdentifierParameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
// A TokenProvider that can be passed to MoQ
|
||||
internal interface ITokenProvider : ITokenValidator, ITokenGenerator
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
public sealed class MockAntiForgeryConfig : IAntiForgeryConfig
|
||||
{
|
||||
public string CookieName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string FormFieldName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool RequireSSL
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool SuppressXFrameOptionsHeader
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
// Convenient class for mocking a ClaimsIdentity instance given some
|
||||
// prefabricated Claim instances.
|
||||
internal sealed class MockClaimsIdentity : ClaimsIdentity
|
||||
{
|
||||
private readonly List<Claim> _claims = new List<Claim>();
|
||||
|
||||
public void AddClaim(string claimType, string value)
|
||||
{
|
||||
_claims.Add(new Claim(claimType, value));
|
||||
}
|
||||
|
||||
public override IEnumerable<Claim> Claims
|
||||
{
|
||||
get { return _claims; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,588 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Security.DataProtection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
public class TokenProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void GenerateCookieToken()
|
||||
{
|
||||
// Arrange
|
||||
var tokenProvider = new TokenProvider(
|
||||
config: null,
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
var retVal = tokenProvider.GenerateCookieToken();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(retVal);
|
||||
}
|
||||
|
||||
[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);
|
||||
|
||||
IAntiForgeryConfig config = new MockAntiForgeryConfig();
|
||||
|
||||
var tokenProvider = new TokenProvider(
|
||||
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.Equal("", fieldToken.Username);
|
||||
Assert.Equal(null, fieldToken.ClaimUid);
|
||||
Assert.Equal("", 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();
|
||||
IAntiForgeryConfig config = new MockAntiForgeryConfig();
|
||||
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
|
||||
var tokenProvider = new TokenProvider(
|
||||
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 "+
|
||||
"'Microsoft.AspNet.Mvc.Core.Test.TokenProviderTest+MyAuthenticatedIdentityWithoutUsername' "+
|
||||
"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");
|
||||
|
||||
IAntiForgeryConfig config = new AntiForgeryConfigWrapper();
|
||||
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
|
||||
var tokenProvider = new TokenProvider(
|
||||
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.Equal("", fieldToken.Username);
|
||||
Assert.Equal(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;
|
||||
ClaimsIdentity identity = new GenericIdentity("some-identity");
|
||||
|
||||
var config = new MockAntiForgeryConfig();
|
||||
|
||||
byte[] data = new byte[256 / 8];
|
||||
CryptRand.FillBuffer(new ArraySegment<byte>(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 TokenProvider(
|
||||
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");
|
||||
|
||||
IAntiForgeryConfig config = new MockAntiForgeryConfig();
|
||||
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
|
||||
|
||||
var tokenProvider = new TokenProvider(
|
||||
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.Equal(null, fieldToken.ClaimUid);
|
||||
Assert.Equal("", fieldToken.AdditionalData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCookieTokenValid_FieldToken_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var cookieToken = new AntiForgeryToken()
|
||||
{
|
||||
IsSessionToken = false
|
||||
};
|
||||
|
||||
var tokenProvider = new TokenProvider(
|
||||
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 TokenProvider(
|
||||
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 TokenProvider(
|
||||
config: null,
|
||||
claimUidExtractor: null,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
|
||||
|
||||
// Assert
|
||||
Assert.True(retVal);
|
||||
}
|
||||
|
||||
[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 MockAntiForgeryConfig()
|
||||
{
|
||||
CookieName = "my-cookie-name"
|
||||
};
|
||||
var tokenProvider = new TokenProvider(
|
||||
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 MockAntiForgeryConfig()
|
||||
{
|
||||
FormFieldName = "my-form-field-name"
|
||||
};
|
||||
|
||||
var tokenProvider = new TokenProvider(
|
||||
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 MockAntiForgeryConfig()
|
||||
{
|
||||
CookieName = "my-cookie-name",
|
||||
FormFieldName = "my-form-field-name"
|
||||
};
|
||||
|
||||
var tokenProvider = new TokenProvider(
|
||||
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 TokenProvider(
|
||||
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;
|
||||
ClaimsIdentity identity = new GenericIdentity(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 TokenProvider(
|
||||
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;
|
||||
ClaimsIdentity identity = new GenericIdentity("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 TokenProvider(
|
||||
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;
|
||||
ClaimsIdentity identity = new GenericIdentity(String.Empty);
|
||||
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 MockAntiForgeryConfig();
|
||||
var tokenProvider = new TokenProvider(
|
||||
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;
|
||||
ClaimsIdentity identity = new GenericIdentity(String.Empty);
|
||||
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 MockAntiForgeryConfig();
|
||||
var tokenProvider = new TokenProvider(
|
||||
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;
|
||||
ClaimsIdentity identity = new GenericIdentity("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 MockAntiForgeryConfig();
|
||||
var tokenProvider = new TokenProvider(
|
||||
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;
|
||||
ClaimsIdentity identity = new GenericIdentity("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 MockAntiForgeryConfig();
|
||||
|
||||
var tokenProvider = new TokenProvider(
|
||||
config: config,
|
||||
claimUidExtractor: mockClaimUidExtractor.Object,
|
||||
additionalDataProvider: null);
|
||||
|
||||
// Act
|
||||
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
|
||||
|
||||
// Assert
|
||||
// Nothing to assert - if we got this far, success!
|
||||
}
|
||||
|
||||
private sealed class MyAuthenticatedIdentityWithoutUsername : ClaimsIdentity
|
||||
{
|
||||
public override bool IsAuthenticated
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return String.Empty; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Microsoft.AspNet.Security.DataProtection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
public class ValidateAntiForgeryTokenAttributeTest
|
||||
{
|
||||
[Fact]
|
||||
public void ValidationAttribute_ForwardsCallToValidateAntiForgeryTokenAuthorizationFilter()
|
||||
{
|
||||
// Arrange
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddInstance<AntiForgery>(GetAntiForgeryInstance());
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
var attribute = new ValidateAntiForgeryTokenAttribute();
|
||||
|
||||
// Act
|
||||
var filter = attribute.CreateInstance(serviceProvider);
|
||||
|
||||
// Assert
|
||||
var validationFilter = filter as ValidateAntiForgeryTokenAuthorizationFilter;
|
||||
Assert.NotNull(validationFilter);
|
||||
}
|
||||
|
||||
private AntiForgery GetAntiForgeryInstance()
|
||||
{
|
||||
var claimExtractor = new Mock<IClaimUidExtractor>();
|
||||
var dataProtectionProvider = new Mock<IDataProtectionProvider>();
|
||||
var additionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
|
||||
return new AntiForgery(claimExtractor.Object,
|
||||
dataProtectionProvider.Object,
|
||||
additionalDataProvider.Object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,12 +26,24 @@
|
|||
<Compile Include="ActionResults\RedirectToActionResultTest.cs" />
|
||||
<Compile Include="ActionResults\RedirectToRouteResultTest.cs" />
|
||||
<Compile Include="ActionSelectionConventionTests.cs" />
|
||||
<Compile Include="AntiXsrf\AntiForgeryTokenSerializerTest.cs" />
|
||||
<Compile Include="AntiXsrf\ITokenProvider.cs" />
|
||||
<Compile Include="AntiXsrf\ValidateAntiForgeryTokenAttributeTest.cs" />
|
||||
<Compile Include="Filters\AuthorizeAttributeTests.cs" />
|
||||
<Compile Include="Filters\AuthorizeAttributeTestsBase.cs" />
|
||||
<Compile Include="AntiXsrf\AntiForgeryTokenStoreTest.cs" />
|
||||
<Compile Include="AntiXsrf\AntiForgeryTokenTest.cs" />
|
||||
<Compile Include="AntiXsrf\AntiForgeryWorkerTests.cs" />
|
||||
<Compile Include="AntiXsrf\BinaryBlobTest.cs" />
|
||||
<Compile Include="AntiXsrf\ClaimUidExtractorTest.cs" />
|
||||
<Compile Include="AntiXsrf\MockAntiForgeryConfig.cs" />
|
||||
<Compile Include="AntiXsrf\MockClaimsIdentity.cs" />
|
||||
<Compile Include="AntiXsrf\TokenProviderTests.cs" />
|
||||
<Compile Include="ControllerTests.cs" />
|
||||
<Compile Include="DefaultActionSelectorTest.cs" />
|
||||
<Compile Include="DefaultControllerAssemblyProviderTests.cs" />
|
||||
<Compile Include="DefaultControllerFactoryTest.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="PropertyHelperTest.cs" />
|
||||
<Compile Include="ReflectedActionInvokerTest.cs" />
|
||||
<Compile Include="Rendering\HtmlAttributePropertyHelperTest.cs" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Http": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.PipelineCore": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Mvc.Core" : "",
|
||||
"Microsoft.AspNet.Mvc" : "",
|
||||
"Microsoft.AspNet.Routing": "0.1-alpha-*",
|
||||
|
|
|
|||
Loading…
Reference in New Issue