Add CookiePolicy Middleware

This commit is contained in:
Hao Kung 2015-09-17 15:37:15 -07:00
parent 1c0768fb71
commit 5cc1fea400
17 changed files with 797 additions and 2 deletions

View File

@ -46,6 +46,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Authorizat
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Authorization", "src\Microsoft.AspNet.Authorization\Microsoft.AspNet.Authorization.xproj", "{6AB3E514-5894-4131-9399-DC7D5284ADDB}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.CookiePolicy", "src\Microsoft.AspNet.CookiePolicy\Microsoft.AspNet.CookiePolicy.xproj", "{86183DC3-02A8-4A68-8B60-71ECEC066E79}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.CookiePolicy.Test", "test\Microsoft.AspNet.CookiePolicy.Test\Microsoft.AspNet.CookiePolicy.Test.xproj", "{1790E052-646F-4529-B90E-6FEA95520D69}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -242,6 +246,30 @@ Global
{6AB3E514-5894-4131-9399-DC7D5284ADDB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{6AB3E514-5894-4131-9399-DC7D5284ADDB}.Release|x86.ActiveCfg = Release|Any CPU
{6AB3E514-5894-4131-9399-DC7D5284ADDB}.Release|x86.Build.0 = Release|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Debug|x86.ActiveCfg = Debug|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Debug|x86.Build.0 = Debug|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Release|Any CPU.Build.0 = Release|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Release|x86.ActiveCfg = Release|Any CPU
{86183DC3-02A8-4A68-8B60-71ECEC066E79}.Release|x86.Build.0 = Release|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Debug|x86.ActiveCfg = Debug|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Debug|x86.Build.0 = Debug|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Release|Any CPU.Build.0 = Release|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Release|x86.ActiveCfg = Release|Any CPU
{1790E052-646F-4529-B90E-6FEA95520D69}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -263,5 +291,7 @@ Global
{2755BFE5-7421-4A31-A644-F817DF5CAA98} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{7AF5AD96-EB6E-4D0E-8ABE-C0B543C0F4C2} = {7BF11F3A-60B6-4796-B504-579C67FFBA34}
{6AB3E514-5894-4131-9399-DC7D5284ADDB} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{86183DC3-02A8-4A68-8B60-71ECEC066E79} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{1790E052-646F-4529-B90E-6FEA95520D69} = {7BF11F3A-60B6-4796-B504-579C67FFBA34}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,23 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.CookiePolicy
{
public class AppendCookieContext
{
public AppendCookieContext(HttpContext context, CookieOptions options, string name, string value)
{
Context = context;
CookieOptions = options;
CookieName = name;
CookieValue = value;
}
public HttpContext Context { get; }
public CookieOptions CookieOptions { get; }
public string CookieName { get; set; }
public string CookieValue { get; set; }
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.CookiePolicy;
namespace Microsoft.AspNet.Builder
{
/// <summary>
/// Extension methods provided by the cookie policy middleware
/// </summary>
public static class CookiePolicyAppBuilderExtensions
{
/// <summary>
/// Adds a cookie policy middleware to your web application pipeline.
/// </summary>
/// <param name="app">The IApplicationBuilder passed to your configuration method</param>
/// <param name="options">The options for the middleware</param>
/// <returns>The original app parameter</returns>
public static IApplicationBuilder UseCookiePolicy(this IApplicationBuilder app, CookiePolicyOptions options)
{
return app.UseMiddleware<CookiePolicyMiddleware>(options);
}
/// <summary>
/// Adds a cookie policy middleware to your web application pipeline.
/// </summary>
/// <param name="app">The IApplicationBuilder passed to your configuration method</param>
/// <param name="configureOptions">Used to configure the options for the middleware</param>
/// <returns>The original app parameter</returns>
public static IApplicationBuilder UseCookiePolicy(this IApplicationBuilder app, Action<CookiePolicyOptions> configureOptions)
{
var options = new CookiePolicyOptions();
if (configureOptions != null)
{
configureOptions(options);
}
return app.UseCookiePolicy(options);
}
}
}

View File

@ -0,0 +1,167 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Internal;
namespace Microsoft.AspNet.CookiePolicy
{
public class CookiePolicyMiddleware
{
private readonly RequestDelegate _next;
public CookiePolicyMiddleware(
RequestDelegate next,
CookiePolicyOptions options)
{
Options = options;
_next = next;
}
public CookiePolicyOptions Options { get; set; }
public Task Invoke(HttpContext context)
{
var feature = context.Features.Get<IResponseCookiesFeature>() ?? new ResponseCookiesFeature(context.Features);
context.Features.Set<IResponseCookiesFeature>(new CookiesWrapperFeature(context, Options, feature));
return _next(context);
}
private class CookiesWrapperFeature : IResponseCookiesFeature
{
public CookiesWrapperFeature(HttpContext context, CookiePolicyOptions options, IResponseCookiesFeature feature)
{
Wrapper = new CookiesWrapper(context, options, feature);
}
public IResponseCookies Wrapper { get; }
public IResponseCookies Cookies
{
get
{
return Wrapper;
}
}
}
private class CookiesWrapper : IResponseCookies
{
public CookiesWrapper(HttpContext context, CookiePolicyOptions options, IResponseCookiesFeature feature)
{
Context = context;
Feature = feature;
Policy = options;
}
public HttpContext Context { get; }
public IResponseCookiesFeature Feature { get; }
public IResponseCookies Cookies
{
get
{
return Feature.Cookies;
}
}
public CookiePolicyOptions Policy { get; }
private bool PolicyRequiresCookieOptions()
{
return Policy.HttpOnly != HttpOnlyPolicy.None || Policy.Secure != SecurePolicy.None;
}
public void Append(string key, string value)
{
if (PolicyRequiresCookieOptions() || Policy.OnAppendCookie != null)
{
Append(key, value, new CookieOptions());
}
else
{
Cookies.Append(key, value);
}
}
public void Append(string key, string value, CookieOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
ApplyPolicy(options);
if (Policy.OnAppendCookie != null)
{
var context = new AppendCookieContext(Context, options, key, value);
Policy.OnAppendCookie(context);
key = context.CookieName;
value = context.CookieValue;
}
Cookies.Append(key, value, options);
}
public void Delete(string key)
{
if (PolicyRequiresCookieOptions() || Policy.OnDeleteCookie != null)
{
Delete(key, new CookieOptions());
}
else
{
Cookies.Delete(key);
}
}
public void Delete(string key, CookieOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
ApplyPolicy(options);
if (Policy.OnDeleteCookie != null)
{
var context = new DeleteCookieContext(Context, options, key);
Policy.OnDeleteCookie(context);
key = context.CookieName;
}
Cookies.Delete(key, options);
}
private void ApplyPolicy(CookieOptions options)
{
switch (Policy.Secure)
{
case SecurePolicy.Always:
options.Secure = true;
break;
case SecurePolicy.SameAsRequest:
options.Secure = Context.Request.IsHttps;
break;
case SecurePolicy.None:
break;
default:
throw new InvalidOperationException();
}
switch (Policy.HttpOnly)
{
case HttpOnlyPolicy.Always:
options.HttpOnly = true;
break;
case HttpOnlyPolicy.None:
break;
default:
throw new InvalidOperationException();
}
}
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.CookiePolicy
{
public class CookiePolicyOptions
{
public HttpOnlyPolicy HttpOnly { get; set; } = HttpOnlyPolicy.None;
public SecurePolicy Secure { get; set; } = SecurePolicy.None;
public Action<AppendCookieContext> OnAppendCookie { get; set; }
public Action<DeleteCookieContext> OnDeleteCookie { get; set; }
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.CookiePolicy
{
public class DeleteCookieContext
{
public DeleteCookieContext(HttpContext context, CookieOptions options, string name)
{
Context = context;
CookieOptions = options;
CookieName = name;
}
public HttpContext Context { get; }
public CookieOptions CookieOptions { get; }
public string CookieName { get; set; }
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.CookiePolicy
{
public enum HttpOnlyPolicy
{
None,
Always
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>86183dc3-02a8-4a68-8b60-71ecec066e79</ProjectGuid>
<RootNamespace>Microsoft.AspNet.CookiePolicy</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Reflection;
using System.Resources;
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: NeutralResourcesLanguage("en-us")]

View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.CookiePolicy
{
public enum SecurePolicy
{
None,
Always,
SameAsRequest
}
}

View File

@ -0,0 +1,15 @@
{
"version": "1.0.0-*",
"description": "ASP.NET 5 cookie policy classes.",
"repository": {
"type": "git",
"url": "git://github.com/aspnet/security"
},
"dependencies": {
"Microsoft.AspNet.Http": "1.0.0-*"
},
"frameworks": {
"dnx451": { },
"dnxcore50": { }
}
}

View File

@ -729,7 +729,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
}
/* [Fact]
[Fact]
public async Task UseCookieWithInstanceDoesntUseSharedOptions()
{
var server = TestServer.Create(app =>
@ -759,7 +759,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
Assert.True(transaction.SetCookie[0].StartsWith("One="));
}*/
}
[Fact]
public async Task MapWithSignInOnlyRedirectToReturnUrlOnLoginPath()

View File

@ -0,0 +1,268 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Internal;
using Microsoft.AspNet.TestHost;
using Xunit;
namespace Microsoft.AspNet.CookiePolicy.Test
{
public class CookiePolicyTests
{
private RequestDelegate SecureCookieAppends = context =>
{
context.Response.Cookies.Append("A", "A");
context.Response.Cookies.Append("B", "B", new CookieOptions { Secure = false });
context.Response.Cookies.Append("C", "C", new CookieOptions());
context.Response.Cookies.Append("D", "D", new CookieOptions { Secure = true });
return Task.FromResult(0);
};
private RequestDelegate HttpCookieAppends = context =>
{
context.Response.Cookies.Append("A", "A");
context.Response.Cookies.Append("B", "B", new CookieOptions { HttpOnly = false });
context.Response.Cookies.Append("C", "C", new CookieOptions());
context.Response.Cookies.Append("D", "D", new CookieOptions { HttpOnly = true });
return Task.FromResult(0);
};
[Fact]
public async Task SecureAlwaysSetsSecure()
{
await RunTest("/secureAlways",
options => options.Secure = SecurePolicy.Always,
SecureCookieAppends,
new RequestTest("http://example.com/secureAlways",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
Assert.Equal("A=A; path=/; secure", transaction.SetCookie[0]);
Assert.Equal("B=B; path=/; secure", transaction.SetCookie[1]);
Assert.Equal("C=C; path=/; secure", transaction.SetCookie[2]);
Assert.Equal("D=D; path=/; secure", transaction.SetCookie[3]);
}));
}
[Fact]
public async Task SecureNoneLeavesSecureUnchanged()
{
await RunTest("/secureNone",
options => options.Secure = SecurePolicy.None,
SecureCookieAppends,
new RequestTest("http://example.com/secureNone",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
Assert.NotNull(transaction.SetCookie);
Assert.Equal("A=A; path=/", transaction.SetCookie[0]);
Assert.Equal("B=B; path=/", transaction.SetCookie[1]);
Assert.Equal("C=C; path=/", transaction.SetCookie[2]);
Assert.Equal("D=D; path=/; secure", transaction.SetCookie[3]);
}));
}
[Fact]
public async Task SecureSameUsesRequest()
{
await RunTest("/secureSame",
options => options.Secure = SecurePolicy.SameAsRequest,
SecureCookieAppends,
new RequestTest("http://example.com/secureSame",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
Assert.Equal("A=A; path=/", transaction.SetCookie[0]);
Assert.Equal("B=B; path=/", transaction.SetCookie[1]);
Assert.Equal("C=C; path=/", transaction.SetCookie[2]);
Assert.Equal("D=D; path=/", transaction.SetCookie[3]);
}),
new RequestTest("https://example.com/secureSame",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
Assert.Equal("A=A; path=/; secure", transaction.SetCookie[0]);
Assert.Equal("B=B; path=/; secure", transaction.SetCookie[1]);
Assert.Equal("C=C; path=/; secure", transaction.SetCookie[2]);
Assert.Equal("D=D; path=/; secure", transaction.SetCookie[3]);
}));
}
[Fact]
public async Task HttpOnlyAlwaysSetsItAlways()
{
await RunTest("/httpOnlyAlways",
options => options.HttpOnly = HttpOnlyPolicy.Always,
HttpCookieAppends,
new RequestTest("http://example.com/httpOnlyAlways",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
Assert.Equal("A=A; path=/; httponly", transaction.SetCookie[0]);
Assert.Equal("B=B; path=/; httponly", transaction.SetCookie[1]);
Assert.Equal("C=C; path=/; httponly", transaction.SetCookie[2]);
Assert.Equal("D=D; path=/; httponly", transaction.SetCookie[3]);
}));
}
[Fact]
public async Task HttpOnlyNoneLeavesItAlone()
{
await RunTest("/httpOnlyNone",
options => options.HttpOnly = HttpOnlyPolicy.None,
HttpCookieAppends,
new RequestTest("http://example.com/httpOnlyNone",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
Assert.Equal("A=A; path=/", transaction.SetCookie[0]);
Assert.Equal("B=B; path=/", transaction.SetCookie[1]);
Assert.Equal("C=C; path=/", transaction.SetCookie[2]);
Assert.Equal("D=D; path=/; httponly", transaction.SetCookie[3]);
}));
}
[Fact]
public async Task CookiePolicyCanHijackAppend()
{
var server = TestServer.Create(app =>
{
app.UseCookiePolicy(options => options.OnAppendCookie = ctx => ctx.CookieName = ctx.CookieValue = "Hao");
app.Run(context =>
{
context.Response.Cookies.Append("A", "A");
context.Response.Cookies.Append("B", "B", new CookieOptions { Secure = false });
context.Response.Cookies.Append("C", "C", new CookieOptions());
context.Response.Cookies.Append("D", "D", new CookieOptions { Secure = true });
return Task.FromResult(0);
});
});
var transaction = await server.SendAsync("http://example.com/login");
Assert.NotNull(transaction.SetCookie);
Assert.Equal("Hao=Hao; path=/", transaction.SetCookie[0]);
Assert.Equal("Hao=Hao; path=/", transaction.SetCookie[1]);
Assert.Equal("Hao=Hao; path=/", transaction.SetCookie[2]);
Assert.Equal("Hao=Hao; path=/; secure", transaction.SetCookie[3]);
}
[Fact]
public async Task CookiePolicyCanHijackDelete()
{
var server = TestServer.Create(app =>
{
app.UseCookiePolicy(options => options.OnDeleteCookie = ctx => ctx.CookieName = "A");
app.Run(context =>
{
context.Response.Cookies.Delete("A");
context.Response.Cookies.Delete("B", new CookieOptions { Secure = false });
context.Response.Cookies.Delete("C", new CookieOptions());
context.Response.Cookies.Delete("D", new CookieOptions { Secure = true });
return Task.FromResult(0);
});
});
var transaction = await server.SendAsync("http://example.com/login");
Assert.NotNull(transaction.SetCookie);
Assert.Equal(1, transaction.SetCookie.Count);
Assert.Equal("A=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/", transaction.SetCookie[0]);
}
[Fact]
public async Task CookiePolicyCallsCookieFeature()
{
var server = TestServer.Create(app =>
{
app.Use(next => context =>
{
context.Features.Set<IResponseCookiesFeature>(new TestCookieFeature());
return next(context);
});
app.UseCookiePolicy(options => options.OnDeleteCookie = ctx => ctx.CookieName = "A");
app.Run(context =>
{
Assert.Throws<NotImplementedException>(() => context.Response.Cookies.Delete("A"));
Assert.Throws<NotImplementedException>(() => context.Response.Cookies.Delete("A", new CookieOptions()));
Assert.Throws<NotImplementedException>(() => context.Response.Cookies.Append("A", "A"));
Assert.Throws<NotImplementedException>(() => context.Response.Cookies.Append("A", "A", new CookieOptions()));
return context.Response.WriteAsync("Done");
});
});
var transaction = await server.SendAsync("http://example.com/login");
Assert.Equal("Done", transaction.ResponseText);
}
private class TestCookieFeature : IResponseCookiesFeature
{
public IResponseCookies Cookies { get; } = new BadCookies();
private class BadCookies : IResponseCookies
{
public void Append(string key, string value)
{
throw new NotImplementedException();
}
public void Append(string key, string value, CookieOptions options)
{
throw new NotImplementedException();
}
public void Delete(string key)
{
throw new NotImplementedException();
}
public void Delete(string key, CookieOptions options)
{
throw new NotImplementedException();
}
}
}
private class RequestTest
{
public RequestTest(string testUri, Action<Transaction> verify)
{
TestUri = testUri;
Verification = verify;
}
public async Task Execute(TestServer server)
{
var transaction = await server.SendAsync(TestUri);
Verification(transaction);
}
public string TestUri { get; set; }
public Action<Transaction> Verification { get; set; }
}
private async Task RunTest(
string path,
Action<CookiePolicyOptions> configureCookiePolicy,
RequestDelegate configureSetup,
params RequestTest[] tests)
{
var server = TestServer.Create(app =>
{
app.Map(path, map =>
{
map.UseCookiePolicy(configureCookiePolicy);
map.Run(configureSetup);
});
});
foreach (var test in tests)
{
await test.Execute(server);
}
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>1790e052-646f-4529-b90e-6fea95520d69</ProjectGuid>
<RootNamespace>Microsoft.AspNet.CookiePolicy.Test</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,74 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.TestHost;
namespace Microsoft.AspNet.CookiePolicy
{
// REVIEW: Should find a shared home for these potentially (Copied from Auth tests)
public static class TestExtensions
{
public const string CookieAuthenticationScheme = "External";
public static async Task<Transaction> SendAsync(this TestServer server, string uri, string cookieHeader = null)
{
var request = new HttpRequestMessage(HttpMethod.Get, uri);
if (!string.IsNullOrEmpty(cookieHeader))
{
request.Headers.Add("Cookie", cookieHeader);
}
var transaction = new Transaction
{
Request = request,
Response = await server.CreateClient().SendAsync(request),
};
if (transaction.Response.Headers.Contains("Set-Cookie"))
{
transaction.SetCookie = transaction.Response.Headers.GetValues("Set-Cookie").ToList();
}
transaction.ResponseText = await transaction.Response.Content.ReadAsStringAsync();
if (transaction.Response.Content != null &&
transaction.Response.Content.Headers.ContentType != null &&
transaction.Response.Content.Headers.ContentType.MediaType == "text/xml")
{
transaction.ResponseElement = XElement.Parse(transaction.ResponseText);
}
return transaction;
}
public static void Describe(this HttpResponse res, ClaimsPrincipal principal)
{
res.StatusCode = 200;
res.ContentType = "text/xml";
var xml = new XElement("xml");
if (principal != null)
{
foreach (var identity in principal.Identities)
{
xml.Add(identity.Claims.Select(claim =>
new XElement("claim", new XAttribute("type", claim.Type),
new XAttribute("value", claim.Value),
new XAttribute("issuer", claim.Issuer))));
}
}
using (var memory = new MemoryStream())
{
using (var writer = new XmlTextWriter(memory, Encoding.UTF8))
{
xml.WriteTo(writer);
}
res.Body.Write(memory.ToArray(), 0, memory.ToArray().Length);
}
}
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Xml.Linq;
namespace Microsoft.AspNet.CookiePolicy
{
// REVIEW: Should find a shared home for these potentially (Copied from Auth tests)
public class Transaction
{
public HttpRequestMessage Request { get; set; }
public HttpResponseMessage Response { get; set; }
public IList<string> SetCookie { get; set; }
public string ResponseText { get; set; }
public XElement ResponseElement { get; set; }
public string AuthenticationCookieValue
{
get
{
if (SetCookie != null && SetCookie.Count > 0)
{
var authCookie = SetCookie.SingleOrDefault(c => c.Contains(".AspNet." + TestExtensions.CookieAuthenticationScheme + "="));
if (authCookie != null)
{
return authCookie.Substring(0, authCookie.IndexOf(';'));
}
}
return null;
}
}
public string FindClaimValue(string claimType, string issuer = null)
{
var claim = ResponseElement.Elements("claim")
.SingleOrDefault(elt => elt.Attribute("type").Value == claimType &&
(issuer == null || elt.Attribute("issuer").Value == issuer));
if (claim == null)
{
return null;
}
return claim.Attribute("value").Value;
}
}
}

View File

@ -0,0 +1,18 @@
{
"compilationOptions": {
"warningsAsErrors": true
},
"dependencies": {
"Microsoft.AspNet.CookiePolicy": "1.0.0-*",
"Microsoft.AspNet.TestHost": "1.0.0-*",
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
"xunit.runner.aspnet": "2.0.0-aspnet-*"
},
"commands": {
"test": "xunit.runner.aspnet"
},
"frameworks": {
"dnx451": { },
"dnxcore50": { }
}
}