/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Mvc/Mvc.ViewFeatures/test/CookieTempDataProviderTest.cs
476 строк
17 KB
github-actions[bot]
Update .NET SDK to 10.0.100-alpha.1.24560.6 (#58862)
18 ноя 2024, 23:34
Не верифицирован
18 ноя 2024, 23:34
3ed133d
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Text; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; using Moq; namespace Microsoft.AspNetCore.Mvc.ViewFeatures; public class CookieTempDataProviderTest { private static readonly byte[] Bytes = Encoding.UTF8.GetBytes("test value"); private static readonly IDictionary<string, object> Dictionary = new Dictionary<string, object> { { "key", "value" }, }; [Fact] public void SaveTempData_UsesCookieName_FromOptions() { // Arrange var expectedCookieName = "TestCookieName"; var expectedDataInCookie = WebEncoders.Base64UrlEncode(Bytes); var tempDataProvider = GetProvider(dataProtector: null, options: new CookieTempDataProviderOptions() { Cookie = { Name = expectedCookieName } }); var responseCookies = new MockResponseCookieCollection(); var httpContext = new DefaultHttpContext(); httpContext.Request.PathBase = "/"; httpContext.Features.Set<IResponseCookiesFeature>(new FakeResponseCookiesFeature(responseCookies)); // Act tempDataProvider.SaveTempData(httpContext, Dictionary); // Assert Assert.Contains(responseCookies, (cookie) => cookie.Key == expectedCookieName); var cookieInfo = responseCookies[expectedCookieName]; Assert.Equal(expectedDataInCookie, cookieInfo.Value); Assert.Equal("/", cookieInfo.Options.Path); } [Fact] public void LoadTempData_ReturnsEmptyDictionary_WhenNoCookieDataIsAvailable() { // Arrange var tempDataProvider = GetProvider(); // Act var tempDataDictionary = tempDataProvider.LoadTempData(new DefaultHttpContext()); // Assert Assert.NotNull(tempDataDictionary); Assert.Empty(tempDataDictionary); } [Fact] public void LoadTempData_ReturnsEmptyDictionary_AndClearsCookie_WhenDataIsInvalid() { // Arrange var dataProtector = new Mock<IDataProtector>(MockBehavior.Strict); dataProtector .Setup(d => d.Unprotect(It.IsAny<byte[]>())) .Throws(new Exception()); var tempDataProvider = GetProvider(dataProtector.Object); var expectedDataToUnprotect = Bytes; var base64AndUrlEncodedDataInCookie = WebEncoders.Base64UrlEncode(expectedDataToUnprotect); var context = new DefaultHttpContext(); context.Request.Headers.Cookie = $"{CookieTempDataProvider.CookieName}={base64AndUrlEncodedDataInCookie}"; // Act var tempDataDictionary = tempDataProvider.LoadTempData(context); // Assert Assert.Empty(tempDataDictionary); var setCookieHeader = SetCookieHeaderValue.Parse(context.Response.Headers["Set-Cookie"].ToString()); Assert.Equal(CookieTempDataProvider.CookieName, setCookieHeader.Name.ToString()); Assert.Equal(string.Empty, setCookieHeader.Value.ToString()); } [Fact] public void LoadTempData_Base64UrlDecodesAnd_UnprotectsData_FromCookie() { // Arrange var expectedDataToUnprotect = Bytes; var base64AndUrlEncodedDataInCookie = WebEncoders.Base64UrlEncode(expectedDataToUnprotect); var dataProtector = new PassThroughDataProtector(); var tempDataProvider = GetProvider(dataProtector); var httpContext = new DefaultHttpContext(); httpContext.Request.Headers.Cookie = $"{CookieTempDataProvider.CookieName}={base64AndUrlEncodedDataInCookie}"; // Act var actualValues = tempDataProvider.LoadTempData(httpContext); // Assert Assert.Equal(expectedDataToUnprotect, dataProtector.DataToUnprotect); Assert.Same(Dictionary, actualValues); } [Fact] public void SaveTempData_ProtectsAnd_Base64UrlEncodesDataAnd_SetsCookie() { // Arrange var values = new Dictionary<string, object>(); values.Add("int", 10); var expectedDataToProtect = Bytes; var expectedDataInCookie = WebEncoders.Base64UrlEncode(expectedDataToProtect); var dataProtector = new PassThroughDataProtector(); var tempDataProvider = GetProvider(dataProtector); var responseCookies = new MockResponseCookieCollection(); var httpContext = new DefaultHttpContext(); httpContext.Request.PathBase = "/"; httpContext.Features.Set<IResponseCookiesFeature>(new FakeResponseCookiesFeature(responseCookies)); // Act tempDataProvider.SaveTempData(httpContext, Dictionary); // Assert Assert.Equal(1, responseCookies.Count); var cookieInfo = responseCookies[CookieTempDataProvider.CookieName]; Assert.NotNull(cookieInfo); Assert.Equal(expectedDataInCookie, cookieInfo.Value); Assert.Equal(expectedDataToProtect, dataProtector.PlainTextToProtect); } [Theory] [InlineData(true, CookieSecurePolicy.None, false)] [InlineData(false, CookieSecurePolicy.None, false)] [InlineData(true, CookieSecurePolicy.Always, true)] [InlineData(false, CookieSecurePolicy.Always, true)] [InlineData(true, CookieSecurePolicy.SameAsRequest, true)] [InlineData(false, CookieSecurePolicy.SameAsRequest, false)] public void SaveTempData_HonorsCookieSecurePolicy_OnOptions( bool isRequestSecure, CookieSecurePolicy cookieSecurePolicy, bool expectedSecureFlag) { // Arrange var expectedDataToProtect = Bytes; var expectedDataInCookie = WebEncoders.Base64UrlEncode(expectedDataToProtect); var dataProtector = new PassThroughDataProtector(); var options = new CookieTempDataProviderOptions(); options.Cookie.SecurePolicy = cookieSecurePolicy; var tempDataProvider = GetProvider(dataProtector, options); var responseCookies = new MockResponseCookieCollection(); var httpContext = new DefaultHttpContext(); httpContext.Request.PathBase = "/"; httpContext.Features.Set<IResponseCookiesFeature>(new FakeResponseCookiesFeature(responseCookies)); httpContext.Request.IsHttps = isRequestSecure; // Act tempDataProvider.SaveTempData(httpContext, Dictionary); // Assert Assert.Equal(1, responseCookies.Count); var cookieInfo = responseCookies[CookieTempDataProvider.CookieName]; Assert.NotNull(cookieInfo); Assert.Equal(expectedDataInCookie, cookieInfo.Value); Assert.Equal(expectedDataToProtect, dataProtector.PlainTextToProtect); Assert.Equal("/", cookieInfo.Options.Path); Assert.Equal(expectedSecureFlag, cookieInfo.Options.Secure); Assert.True(cookieInfo.Options.HttpOnly); Assert.Null(cookieInfo.Options.Expires); Assert.Null(cookieInfo.Options.Domain); } [Theory] [InlineData(null, "/")] [InlineData("", "/")] [InlineData("/", "/")] [InlineData("/vdir1", "/vdir1")] public void SaveTempData_DefaultProviderOptions_SetsCookie_WithAppropriateCookieOptions( string pathBase, string expectedCookiePath) { // Arrange var expectedDataToProtect = Bytes; var expectedDataInCookie = WebEncoders.Base64UrlEncode(expectedDataToProtect); var dataProtector = new PassThroughDataProtector(); var tempDataProvider = GetProvider(dataProtector); var responseCookies = new MockResponseCookieCollection(); var httpContext = new DefaultHttpContext(); httpContext.Request.PathBase = pathBase; httpContext.Features.Set<IResponseCookiesFeature>(new FakeResponseCookiesFeature(responseCookies)); // Act tempDataProvider.SaveTempData(httpContext, Dictionary); // Assert Assert.Equal(1, responseCookies.Count); var cookieInfo = responseCookies[CookieTempDataProvider.CookieName]; Assert.NotNull(cookieInfo); Assert.Equal(expectedDataInCookie, cookieInfo.Value); Assert.Equal(expectedDataToProtect, dataProtector.PlainTextToProtect); Assert.Equal(expectedCookiePath, cookieInfo.Options.Path); Assert.False(cookieInfo.Options.Secure); Assert.True(cookieInfo.Options.HttpOnly); Assert.Null(cookieInfo.Options.Expires); Assert.Null(cookieInfo.Options.Domain); } [Theory] [InlineData(null, null, null, "/", null)] [InlineData("", null, null, "/", null)] [InlineData("/", null, null, "/", null)] [InlineData("/", "/vdir1", null, "/vdir1", null)] [InlineData("/", "/vdir1", ".abc.com", "/vdir1", ".abc.com")] [InlineData("/vdir1", "/", ".abc.com", "/", ".abc.com")] public void SaveTempData_CustomProviderOptions_SetsCookie_WithAppropriateCookieOptions( string requestPathBase, string optionsPath, string optionsDomain, string expectedCookiePath, string expectedDomain) { // Arrange var expectedDataToProtect = Bytes; var expectedDataInCookie = WebEncoders.Base64UrlEncode(expectedDataToProtect); var dataProtector = new PassThroughDataProtector(); var tempDataProvider = GetProvider( dataProtector, new CookieTempDataProviderOptions { Cookie = { Path = optionsPath, Domain = optionsDomain } }); var responseCookies = new MockResponseCookieCollection(); var httpContext = new DefaultHttpContext(); httpContext.Request.PathBase = requestPathBase; httpContext.Features.Set<IResponseCookiesFeature>(new FakeResponseCookiesFeature(responseCookies)); // Act tempDataProvider.SaveTempData(httpContext, Dictionary); // Assert Assert.Equal(1, responseCookies.Count); var cookieInfo = responseCookies[CookieTempDataProvider.CookieName]; Assert.NotNull(cookieInfo); Assert.Equal(expectedDataInCookie, cookieInfo.Value); Assert.Equal(expectedDataToProtect, dataProtector.PlainTextToProtect); Assert.Equal(expectedCookiePath, cookieInfo.Options.Path); Assert.Equal(expectedDomain, cookieInfo.Options.Domain); Assert.False(cookieInfo.Options.Secure); Assert.True(cookieInfo.Options.HttpOnly); Assert.Null(cookieInfo.Options.Expires); } [Fact] public void SaveTempData_RemovesCookie_WhenNoDataToSave() { // Arrange var values = new Dictionary<string, object>(); var serializedData = Bytes; var base64AndUrlEncodedData = WebEncoders.Base64UrlEncode(serializedData); var dataProtector = new PassThroughDataProtector(); var tempDataProvider = GetProvider(dataProtector); var requestCookies = $"{CookieTempDataProvider.CookieName}={base64AndUrlEncodedData}"; var httpContext = new DefaultHttpContext(); httpContext.Request.PathBase = "/"; httpContext.Request.Headers.Cookie = requestCookies; // Act tempDataProvider.SaveTempData(httpContext, new Dictionary<string, object>()); // Assert var responseCookies = httpContext.Response.GetTypedHeaders().SetCookie; Assert.Single(responseCookies); var cookie = responseCookies.Single(); Assert.NotNull(cookie); Assert.Equal(CookieTempDataProvider.CookieName, cookie.Name.AsSpan()); Assert.Equal(string.Empty, cookie.Value.AsSpan()); Assert.NotNull(cookie.Expires); Assert.True(cookie.Expires.Value < DateTimeOffset.Now); // expired cookie } [Fact] public void SaveAndLoad_Works() { // Arrange var testProvider = GetProvider(); var input = new Dictionary<string, object> { { "string", "value" } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); UpdateRequestWithCookies(context); var tempData = testProvider.LoadTempData(context); // Assert Assert.Same(Dictionary, tempData); } private static HttpContext GetHttpContext() { var context = new DefaultHttpContext(); context.Request.PathBase = "/"; return context; } private void UpdateRequestWithCookies(HttpContext httpContext) { var responseCookies = httpContext.Response.GetTypedHeaders().SetCookie; if (responseCookies.Count > 0) { var stringBuilder = new StringBuilder(); foreach (var cookie in responseCookies) { stringBuilder.Append(cookie.Name.AsSpan()); stringBuilder.Append("="); stringBuilder.Append(cookie.Value.AsSpan()); stringBuilder.Append(";"); } httpContext.Request.Headers.Cookie = stringBuilder.ToString(); } } private class MockResponseCookieCollection : IResponseCookies, IEnumerable<CookieInfo> { private readonly Dictionary<string, CookieInfo> _cookies = new Dictionary<string, CookieInfo>(StringComparer.OrdinalIgnoreCase); public int Count { get { return _cookies.Count; } } public CookieInfo this[string key] => _cookies[key]; public void Append(string key, string value, CookieOptions options) { _cookies[key] = new CookieInfo() { Key = key, Value = value, Options = options }; } public void Append(string key, string value) { Append(key, value, new CookieOptions()); } public void Delete(string key, CookieOptions options) { _cookies.Remove(key); } public void Delete(string key) { _cookies.Remove(key); } public IEnumerator<CookieInfo> GetEnumerator() { return _cookies.Values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } private CookieTempDataProvider GetProvider(IDataProtector dataProtector = null, CookieTempDataProviderOptions options = null) { if (dataProtector == null) { dataProtector = new PassThroughDataProtector(); } if (options == null) { options = new CookieTempDataProviderOptions(); } var testOptions = new Mock<IOptions<CookieTempDataProviderOptions>>(); testOptions.SetupGet(o => o.Value).Returns(options); return new CookieTempDataProvider( new PassThroughDataProtectionProvider(dataProtector), NullLoggerFactory.Instance, testOptions.Object, new TestTempDataSerializer()); } private class PassThroughDataProtectionProvider : IDataProtectionProvider { private readonly IDataProtector _dataProtector; public PassThroughDataProtectionProvider(IDataProtector dataProtector) { _dataProtector = dataProtector; } public IDataProtector CreateProtector(string purpose) { return _dataProtector; } } private class PassThroughDataProtector : IDataProtector { public byte[] DataToUnprotect { get; private set; } public byte[] PlainTextToProtect { get; private set; } public string Purpose { get; private set; } public IDataProtector CreateProtector(string purpose) { Purpose = purpose; return this; } public byte[] Protect(byte[] plaintext) { PlainTextToProtect = plaintext; return PlainTextToProtect; } public byte[] Unprotect(byte[] protectedData) { DataToUnprotect = protectedData; return DataToUnprotect; } } private class CookieInfo { public string Key { get; set; } public string Value { get; set; } public CookieOptions Options { get; set; } } private class TestTempDataSerializer : TempDataSerializer { public override IDictionary<string, object> Deserialize(byte[] unprotectedData) { return Dictionary; } public override byte[] Serialize(IDictionary<string, object> values) { return Bytes; } } private class FakeResponseCookiesFeature(IResponseCookies cookies) : IResponseCookiesFeature { public IResponseCookies Cookies => cookies; } }