Files
acceptance-tests
data
debian
docs
external
Newtonsoft.Json
api-doc-tools
api-snapshot
aspnetwebstack
packages
src
test
Microsoft.TestCommon
Microsoft.Web.Helpers.Test
Microsoft.Web.Http.Data.Test
Microsoft.Web.Mvc.Test
Microsoft.Web.WebPages.OAuth.Test
SPA.Test
System.Json.Test.Integration
System.Json.Test.Unit
System.Net.Http.Formatting.Test.Integration
System.Net.Http.Formatting.Test.Unit
System.Web.Helpers.Test
System.Web.Http.Integration.Test
System.Web.Http.SelfHost.Test
System.Web.Http.Test
System.Web.Http.WebHost.Test
System.Web.Mvc.Test
System.Web.Razor.Test
System.Web.WebPages.Administration.Test
System.Web.WebPages.Deployment.Test
System.Web.WebPages.Razor.Test
System.Web.WebPages.Test
ApplicationParts
Extensions
Helpers
AntiXsrf
AntiForgeryTokenSerializerTest.cs
AntiForgeryTokenStoreTest.cs
AntiForgeryTokenTest.cs
AntiForgeryWorkerTest.cs
BinaryBlobTest.cs
ClaimUidExtractorTest.cs
HexUtil.cs
MachineKeyCryptoSystemTest.cs
MockAntiForgeryConfig.cs
MockableAntiForgeryTokenSerializer.cs
MockableClaimUidExtractor.cs
MockableCryptoSystem.cs
MockableTokenStore.cs
MockableTokenValidator.cs
TokenValidatorTest.cs
Claims
AntiForgeryConfigTest.cs
AntiForgeryTest.cs
CryptoUtilTest.cs
UnvalidatedRequestValuesTest.cs
Html
Instrumentation
Mvc
Properties
ScopeStorage
TestFiles
Utils
Validation
WebPage
App.config
PreApplicationStartCodeTest.cs
System.Web.WebPages.Test.csproj
packages.config
WebMatrix.Data.Test
WebMatrix.WebData.Test
Settings.StyleCop
tools
.gitattributes
.gitignore
License.txt
README.md
Runtime.msbuild
Runtime.sln
Runtime.xunit
Settings.StyleCop
build.cmd
binary-reference-assemblies
bockbuild
boringssl
cecil
cecil-legacy
corefx
corert
ikdasm
ikvm
linker
nuget-buildtasks
nunit-lite
roslyn-binaries
rx
xunit-binaries
ikvm-native
libgc
llvm
m4
man
mcs
mk
mono
msvc
po
runtime
samples
scripts
support
tools
COPYING.LIB
LICENSE
Makefile.am
Makefile.in
NEWS
README.md
acinclude.m4
aclocal.m4
autogen.sh
code_of_conduct.md
compile
config.guess
config.h.in
config.rpath
config.sub
configure.REMOVED.git-id
configure.ac.REMOVED.git-id
depcomp
install-sh
ltmain.sh.REMOVED.git-id
missing
mkinstalldirs
mono-uninstalled.pc.in
test-driver
winconfig.h

518 lines
23 KiB
C#
Raw Normal View History

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Security.Principal;
using System.Web.Helpers.Test;
using System.Web.Mvc;
using Moq;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Helpers.AntiXsrf.Test
{
public class TokenValidatorTest
{
[Fact]
public void GenerateCookieToken()
{
// Arrange
TokenValidator tokenValidator = new TokenValidator(
config: null,
claimUidExtractor: null);
// Act
AntiForgeryToken retVal = tokenValidator.GenerateCookieToken();
// Assert
Assert.NotNull(retVal);
}
[Fact]
public void GenerateFormToken_AnonymousUser()
{
// Arrange
AntiForgeryToken cookieToken = new AntiForgeryToken() { IsSessionToken = true };
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
Mock<IIdentity> mockIdentity = new Mock<IIdentity>();
mockIdentity.Setup(o => o.IsAuthenticated).Returns(false);
IAntiForgeryConfig config = new MockAntiForgeryConfig();
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: null);
// Act
var fieldToken = validator.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
AntiForgeryToken cookieToken = new AntiForgeryToken()
{
IsSessionToken = true
};
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new MyAuthenticatedIdentityWithoutUsername();
IAntiForgeryConfig config = new MockAntiForgeryConfig();
IClaimUidExtractor claimUidExtractor = new Mock<MockableClaimUidExtractor>().Object;
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: claimUidExtractor);
// Act & assert
var ex = Assert.Throws<InvalidOperationException>(() => validator.GenerateFormToken(httpContext, identity, cookieToken));
Assert.Equal(@"The provided identity of type 'System.Web.Helpers.AntiXsrf.Test.TokenValidatorTest+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 setting the static property AntiForgeryConfig.AdditionalDataProvider to an instance of a type that can provide some form of unique identifier for the current user.", ex.Message);
}
[Fact]
public void GenerateFormToken_AuthenticatedWithoutUsernameAndNoAdditionalData_NoAdditionalData_SuppressHeuristics()
{
// Arrange
AntiForgeryToken cookieToken = new AntiForgeryToken() { IsSessionToken = true };
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new MyAuthenticatedIdentityWithoutUsername();
IAntiForgeryConfig config = new MockAntiForgeryConfig()
{
SuppressIdentityHeuristicChecks = true
};
IClaimUidExtractor claimUidExtractor = new Mock<MockableClaimUidExtractor>().Object;
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: claimUidExtractor);
// Act
var fieldToken = validator.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("", fieldToken.AdditionalData);
}
[Fact]
public void GenerateFormToken_AuthenticatedWithoutUsername_WithAdditionalData()
{
// Arrange
AntiForgeryToken cookieToken = new AntiForgeryToken() { IsSessionToken = true };
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new MyAuthenticatedIdentityWithoutUsername();
Mock<IAntiForgeryAdditionalDataProvider> mockAdditionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.GetAdditionalData(httpContext)).Returns("additional-data");
IAntiForgeryConfig config = new MockAntiForgeryConfig()
{
AdditionalDataProvider = mockAdditionalDataProvider.Object
};
IClaimUidExtractor claimUidExtractor = new Mock<MockableClaimUidExtractor>().Object;
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: claimUidExtractor);
// Act
var fieldToken = validator.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
AntiForgeryToken cookieToken = new AntiForgeryToken() { IsSessionToken = true };
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new GenericIdentity("some-identity");
MockAntiForgeryConfig config = new MockAntiForgeryConfig()
{
UniqueClaimTypeIdentifier = "unique-identifier"
};
BinaryBlob expectedClaimUid = new BinaryBlob(256);
Mock<MockableClaimUidExtractor> mockClaimUidExtractor = new Mock<MockableClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)).Returns((object)expectedClaimUid);
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: mockClaimUidExtractor.Object);
// Act
var fieldToken = validator.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
AntiForgeryToken cookieToken = new AntiForgeryToken() { IsSessionToken = true };
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
Mock<IIdentity> mockIdentity = new Mock<IIdentity>();
mockIdentity.Setup(o => o.IsAuthenticated).Returns(true);
mockIdentity.Setup(o => o.Name).Returns("my-username");
IAntiForgeryConfig config = new MockAntiForgeryConfig();
IClaimUidExtractor claimUidExtractor = new Mock<MockableClaimUidExtractor>().Object;
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: claimUidExtractor);
// Act
var fieldToken = validator.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
AntiForgeryToken cookieToken = new AntiForgeryToken()
{
IsSessionToken = false
};
TokenValidator validator = new TokenValidator(
config: null,
claimUidExtractor: null);
// Act
bool retVal = validator.IsCookieTokenValid(cookieToken);
// Assert
Assert.False(retVal);
}
[Fact]
public void IsCookieTokenValid_NullToken_ReturnsFalse()
{
// Arrange
AntiForgeryToken cookieToken = null;
TokenValidator validator = new TokenValidator(
config: null,
claimUidExtractor: null);
// Act
bool retVal = validator.IsCookieTokenValid(cookieToken);
// Assert
Assert.False(retVal);
}
[Fact]
public void IsCookieTokenValid_ValidToken_ReturnsTrue()
{
// Arrange
AntiForgeryToken cookieToken = new AntiForgeryToken()
{
IsSessionToken = true
};
TokenValidator validator = new TokenValidator(
config: null,
claimUidExtractor: null);
// Act
bool retVal = validator.IsCookieTokenValid(cookieToken);
// Assert
Assert.True(retVal);
}
[Fact]
public void ValidateTokens_SessionTokenMissing()
{
// Arrange
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new Mock<IIdentity>().Object;
AntiForgeryToken sessionToken = null;
AntiForgeryToken fieldtoken = new AntiForgeryToken() { IsSessionToken = false };
MockAntiForgeryConfig config = new MockAntiForgeryConfig()
{
CookieName = "my-cookie-name"
};
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: null);
// Act & assert
var ex = Assert.Throws<HttpAntiForgeryException>(() => validator.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
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new Mock<IIdentity>().Object;
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = null;
MockAntiForgeryConfig config = new MockAntiForgeryConfig()
{
FormFieldName = "my-form-field-name"
};
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: null);
// Act & assert
var ex = Assert.Throws<HttpAntiForgeryException>(() => validator.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
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new Mock<IIdentity>().Object;
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = new AntiForgeryToken() { IsSessionToken = false };
MockAntiForgeryConfig config = new MockAntiForgeryConfig()
{
CookieName = "my-cookie-name",
FormFieldName = "my-form-field-name"
};
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: null);
// Act & assert
var ex1 = Assert.Throws<HttpAntiForgeryException>(() => validator.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<HttpAntiForgeryException>(() => validator.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
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new Mock<IIdentity>().Object;
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = new AntiForgeryToken() { IsSessionToken = false };
TokenValidator validator = new TokenValidator(
config: null,
claimUidExtractor: null);
// Act & assert
var ex = Assert.Throws<HttpAntiForgeryException>(() => validator.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
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new GenericIdentity(identityUsername);
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = new AntiForgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = embeddedUsername, IsSessionToken = false };
Mock<MockableClaimUidExtractor> mockClaimUidExtractor = new Mock<MockableClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)).Returns((object)null);
TokenValidator validator = new TokenValidator(
config: null,
claimUidExtractor: mockClaimUidExtractor.Object);
// Act & assert
var ex = Assert.Throws<HttpAntiForgeryException>(() => validator.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
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new GenericIdentity("the-user");
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = new AntiForgeryToken() { SecurityToken = sessionToken.SecurityToken, IsSessionToken = false, ClaimUid = new BinaryBlob(256) };
Mock<MockableClaimUidExtractor> mockClaimUidExtractor = new Mock<MockableClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)).Returns(new BinaryBlob(256));
TokenValidator validator = new TokenValidator(
config: null,
claimUidExtractor: mockClaimUidExtractor.Object);
// Act & assert
var ex = Assert.Throws<HttpAntiForgeryException>(() => validator.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
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new GenericIdentity(String.Empty);
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = new AntiForgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = String.Empty, IsSessionToken = false, AdditionalData = "some-additional-data" };
Mock<IAntiForgeryAdditionalDataProvider> mockAdditionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data")).Returns(false);
MockAntiForgeryConfig config = new MockAntiForgeryConfig()
{
AdditionalDataProvider = mockAdditionalDataProvider.Object
};
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: null);
// Act & assert
var ex = Assert.Throws<HttpAntiForgeryException>(() => validator.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
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new GenericIdentity(String.Empty);
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = new AntiForgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = String.Empty, IsSessionToken = false, AdditionalData = "some-additional-data" };
Mock<IAntiForgeryAdditionalDataProvider> mockAdditionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data")).Returns(true);
MockAntiForgeryConfig config = new MockAntiForgeryConfig()
{
AdditionalDataProvider = mockAdditionalDataProvider.Object
};
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: null);
// Act
validator.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
[Fact]
public void ValidateTokens_Success_AuthenticatedUserWithUsername()
{
// Arrange
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new GenericIdentity("the-user");
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = new AntiForgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = "THE-USER", IsSessionToken = false, AdditionalData = "some-additional-data" };
Mock<IAntiForgeryAdditionalDataProvider> mockAdditionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data")).Returns(true);
MockAntiForgeryConfig config = new MockAntiForgeryConfig()
{
AdditionalDataProvider = mockAdditionalDataProvider.Object
};
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: new Mock<MockableClaimUidExtractor>().Object);
// Act
validator.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
[Fact]
public void ValidateTokens_Success_ClaimsBasedUser()
{
// Arrange
HttpContextBase httpContext = new Mock<HttpContextBase>().Object;
IIdentity identity = new GenericIdentity("the-user");
AntiForgeryToken sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = new AntiForgeryToken() { SecurityToken = sessionToken.SecurityToken, IsSessionToken = false, ClaimUid = new BinaryBlob(256) };
Mock<MockableClaimUidExtractor> mockClaimUidExtractor = new Mock<MockableClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)).Returns(fieldtoken.ClaimUid);
MockAntiForgeryConfig config = new MockAntiForgeryConfig();
TokenValidator validator = new TokenValidator(
config: config,
claimUidExtractor: mockClaimUidExtractor.Object);
// Act
validator.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
private sealed class MyAuthenticatedIdentityWithoutUsername : IIdentity
{
public string AuthenticationType
{
get { throw new NotImplementedException(); }
}
public bool IsAuthenticated
{
get { return true; }
}
public string Name
{
get { return String.Empty; }
}
}
}
}