Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Add new CookiePolicy middleware #452

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Security.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
23 changes: 23 additions & 0 deletions src/Microsoft.AspNet.CookiePolicy/AppendCookieContext.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
167 changes: 167 additions & 0 deletions src/Microsoft.AspNet.CookiePolicy/CookiePolicyMiddleware.cs
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

argument validation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There didn't appear to be any argument validation in the base HttpAbstractions ResponseCookies so I just mimiced that behavior since we are just wrapping no?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's at least a NotNull check on 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();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default throw?

}
}
}
}
16 changes: 16 additions & 0 deletions src/Microsoft.AspNet.CookiePolicy/CookiePolicyOptions.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
21 changes: 21 additions & 0 deletions src/Microsoft.AspNet.CookiePolicy/DeleteCookieContext.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
11 changes: 11 additions & 0 deletions src/Microsoft.AspNet.CookiePolicy/HttpOnlyPolicy.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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>
8 changes: 8 additions & 0 deletions src/Microsoft.AspNet.CookiePolicy/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -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")]
12 changes: 12 additions & 0 deletions src/Microsoft.AspNet.CookiePolicy/SecurePolicy.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading