Authorization middleware (#1894)

This commit is contained in:
James Newton-King 2018-11-08 15:42:35 +13:00 committed by GitHub
parent 32177cad1e
commit cb83e4f485
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1124 additions and 65 deletions

1
.gitignore vendored
View File

@ -30,3 +30,4 @@ project.lock.json
/.vs/
.vscode/
global.json
BenchmarkDotNet.Artifacts/

View File

@ -79,6 +79,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authen
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WsFedSample", "samples\WsFedSample\WsFedSample.csproj", "{5EC2E398-E46A-430D-8E4B-E91C8FC3E800}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{55052FE3-F8C2-4E6C-97B0-C02ED1DBEA62}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Security.Performance", "benchmarks\Microsoft.AspNetCore.Security.Performance\Microsoft.AspNetCore.Security.Performance.csproj", "{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -517,6 +521,22 @@ Global
{5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|x64.Build.0 = Release|Any CPU
{5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|x86.ActiveCfg = Release|Any CPU
{5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|x86.Build.0 = Release|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|x64.ActiveCfg = Debug|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|x64.Build.0 = Debug|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|x86.ActiveCfg = Debug|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|x86.Build.0 = Debug|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|Any CPU.Build.0 = Release|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|x64.ActiveCfg = Release|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|x64.Build.0 = Release|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|x86.ActiveCfg = Release|Any CPU
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -549,6 +569,7 @@ Global
{24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
{B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{5EC2E398-E46A-430D-8E4B-E91C8FC3E800} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A} = {55052FE3-F8C2-4E6C-97B0-C02ED1DBEA62}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ABF8089E-43D0-4010-84A7-7A9DCFE49357}

View File

@ -0,0 +1,53 @@
// 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 BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Security
{
public class AuthorizationMiddlewareBenchmark
{
private AuthorizationMiddleware _authorizationMiddleware;
private DefaultHttpContext _httpContextNoEndpoint;
private DefaultHttpContext _httpContextHasEndpoint;
[GlobalSetup]
public void Setup()
{
var policyProvider = new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions()));
_authorizationMiddleware = new AuthorizationMiddleware((context) => Task.CompletedTask, policyProvider);
_httpContextNoEndpoint = new DefaultHttpContext();
var feature = new EndpointFeature
{
Endpoint = new Endpoint((context) => Task.CompletedTask, EndpointMetadataCollection.Empty, "Test endpoint")
};
_httpContextHasEndpoint = new DefaultHttpContext();
_httpContextHasEndpoint.Features.Set<IEndpointFeature>(feature);
}
[Benchmark]
public Task Invoke_NoEndpoint_NoAuthorization()
{
return _authorizationMiddleware.Invoke(_httpContextNoEndpoint);
}
[Benchmark]
public Task Invoke_HasEndpoint_NoAuthorization()
{
return _authorizationMiddleware.Invoke(_httpContextHasEndpoint);
}
private class EndpointFeature : IEndpointFeature
{
public Endpoint Endpoint { get; set; }
}
}
}

View File

@ -0,0 +1,28 @@
// 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 BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Security
{
public class AuthorizationPolicyBenchmark
{
private DefaultAuthorizationPolicyProvider _policyProvider;
[GlobalSetup]
public void Setup()
{
_policyProvider = new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions()));
}
[Benchmark]
public Task CombineAsync()
{
return AuthorizationPolicy.CombineAsync(_policyProvider, Array.Empty<IAuthorizeData>());
}
}
}

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>Exe</OutputType>
<ServerGarbageCollection>true</ServerGarbageCollection>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
<RootNamespace>Microsoft.AspNetCore.Security</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Authorization.Policy\Microsoft.AspNetCore.Authorization.Policy.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="$(BenchmarkDotNetPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1 @@
[assembly: BenchmarkDotNet.Attributes.AspNetCoreBenchmark]

View File

@ -0,0 +1,16 @@
Compile the solution in Release mode (so binaries are available in release)
To run a specific benchmark add it as parameter.
```
dotnet run -c Release --framework <tfm> <benchmark_name>
```
To run all benchmarks use '*' as the name.
```
dotnet run -c Release --framework <tfm> *
```
If you run without any parameters, you'll be offered the list of all benchmarks and get to choose.
```
dotnet run -c Release --framework <tfm>
```

View File

@ -3,46 +3,51 @@
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>
<PropertyGroup Label="Package Versions">
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<InternalAspNetCoreSdkPackageVersion>3.0.0-alpha1-20181004.7</InternalAspNetCoreSdkPackageVersion>
<MicrosoftAspNetCoreAuthenticationAbstractionsPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreAuthenticationAbstractionsPackageVersion>
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
<MicrosoftAspNetCoreDataProtectionExtensionsPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreDataProtectionExtensionsPackageVersion>
<MicrosoftAspNetCoreDataProtectionPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreDataProtectionPackageVersion>
<MicrosoftAspNetCoreDiagnosticsPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreDiagnosticsPackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
<MicrosoftAspNetCoreServerKestrelHttpsPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreServerKestrelHttpsPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreStaticFilesPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreStaticFilesPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>3.0.0-alpha1-10584</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>3.0.0-alpha1-10584</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>3.14.2</MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>
<MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>5.2.0</MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>
<MicrosoftIdentityModelProtocolsWsFederationPackageVersion>5.2.0</MicrosoftIdentityModelProtocolsWsFederationPackageVersion>
<MicrosoftAspNetCoreAuthenticationAbstractionsPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreAuthenticationAbstractionsPackageVersion>
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>3.0.0-alpha1-10657</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreDataProtectionExtensionsPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreDataProtectionExtensionsPackageVersion>
<MicrosoftAspNetCoreDataProtectionPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreDataProtectionPackageVersion>
<MicrosoftAspNetCoreDiagnosticsPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreDiagnosticsPackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>3.0.0-a-alpha1-endpoint-extensions-namespace-16778</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreRoutingPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreRoutingPackageVersion>
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
<MicrosoftAspNetCoreServerKestrelHttpsPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreServerKestrelHttpsPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreStaticFilesPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreStaticFilesPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>3.0.0-alpha1-10706</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>3.0.0-preview-181105-05</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>3.0.0-preview-181105-05</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>3.0.0-preview-181105-05</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>3.0.0-preview-181105-05</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>3.0.0-alpha1-10706</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>3.19.8</MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>
<MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>5.3.0</MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>
<MicrosoftIdentityModelProtocolsWsFederationPackageVersion>5.3.0</MicrosoftIdentityModelProtocolsWsFederationPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.3</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview3-27001-02</MicrosoftNETCoreApp22PackageVersion>
<MicrosoftNETCoreApp22PackageVersion>2.2.0-rtm-27105-02</MicrosoftNETCoreApp22PackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MicrosoftOwinSecurityCookiesPackageVersion>3.0.1</MicrosoftOwinSecurityCookiesPackageVersion>
<MicrosoftOwinSecurityPackageVersion>3.0.1</MicrosoftOwinSecurityPackageVersion>
<MicrosoftOwinTestingPackageVersion>3.0.1</MicrosoftOwinTestingPackageVersion>
<MoqPackageVersion>4.10.0</MoqPackageVersion>
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
<SystemIdentityModelTokensJwtPackageVersion>5.2.0</SystemIdentityModelTokensJwtPackageVersion>
<SystemIdentityModelTokensJwtPackageVersion>5.3.0</SystemIdentityModelTokensJwtPackageVersion>
<XunitAnalyzersPackageVersion>0.10.0</XunitAnalyzersPackageVersion>
<XunitPackageVersion>2.3.1</XunitPackageVersion>
<XunitRunnerVisualStudioPackageVersion>2.4.0</XunitRunnerVisualStudioPackageVersion>

View File

@ -0,0 +1,29 @@
// 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.AspNetCore.Authorization;
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Extension methods to add authorization capabilities to an HTTP application pipeline.
/// </summary>
public static class AuthorizationAppBuilderExtensions
{
/// <summary>
/// Adds the <see cref="AuthorizationMiddleware"/> to the specified <see cref="IApplicationBuilder"/>, which enables authorization capabilities.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IApplicationBuilder UseAuthorization(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<AuthorizationMiddleware>();
}
}
}

View File

@ -0,0 +1,50 @@
// 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.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Routing;
namespace Microsoft.AspNetCore.Builder
{
public static class AuthorizationEndpointConventionBuilderExtensions
{
public static IEndpointConventionBuilder RequireAuthorization(this IEndpointConventionBuilder builder, params IAuthorizeData[] authorizeData)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (authorizeData == null)
{
throw new ArgumentNullException(nameof(authorizeData));
}
builder.Apply(endpointBuilder =>
{
foreach (var data in authorizeData)
{
endpointBuilder.Metadata.Add(data);
}
});
return builder;
}
public static IEndpointConventionBuilder RequireAuthorization(this IEndpointConventionBuilder builder, params string[] policyNames)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (policyNames == null)
{
throw new ArgumentNullException(nameof(policyNames));
}
return builder.RequireAuthorization(policyNames.Select(n => new AuthorizeAttribute(n)).ToArray());
}
}
}

View File

@ -0,0 +1,104 @@
// 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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Endpoints;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Authorization
{
public class AuthorizationMiddleware
{
private readonly RequestDelegate _next;
private readonly IAuthorizationPolicyProvider _policyProvider;
public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (policyProvider == null)
{
throw new ArgumentNullException(nameof(policyProvider));
}
_next = next;
_policyProvider = policyProvider;
}
public async Task Invoke(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var endpoint = context.GetEndpoint();
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
if (policy == null)
{
await _next(context);
return;
}
// Policy evaluator has transient lifetime so it fetched from request services instead of injecting in constructor
var policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();
var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context);
// Allow Anonymous skips all authorization
if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
{
await _next(context);
return;
}
// Note that the resource will be null if there is no matched endpoint
var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
if (authorizeResult.Challenged)
{
if (policy.AuthenticationSchemes.Any())
{
foreach (var scheme in policy.AuthenticationSchemes)
{
await context.ChallengeAsync(scheme);
}
}
else
{
await context.ChallengeAsync();
}
return;
}
else if (authorizeResult.Forbidden)
{
if (policy.AuthenticationSchemes.Any())
{
foreach (var scheme in policy.AuthenticationSchemes)
{
await context.ForbidAsync(scheme);
}
}
else
{
await context.ForbidAsync();
}
return;
}
await _next(context);
}
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<Description>ASP.NET Core authorization policy helper classes.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn);CS1591;NU1605</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;authorization</PackageTags>
</PropertyGroup>
@ -14,6 +14,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="$(MicrosoftAspNetCoreAuthenticationAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(MicrosoftAspNetCoreHttpAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
</ItemGroup>

View File

@ -20,13 +20,25 @@ namespace Microsoft.AspNetCore.Authorization
public bool InvokeHandlersAfterFailure { get; set; } = true;
/// <summary>
/// Gets or sets the default authorization policy.
/// Gets or sets the default authorization policy. Defaults to require authenticated users.
/// </summary>
/// <remarks>
/// The default policy is to require any authenticated user.
/// The default policy used when evaluating <see cref="IAuthorizeData"/> with no policy name specified.
/// </remarks>
public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
/// <summary>
/// Gets or sets the required authorization policy. Defaults to null.
/// </summary>
/// <remarks>
/// By default the required policy is null.
///
/// If a required policy has been specified then it is always evaluated, even if there are no
/// <see cref="IAuthorizeData"/> instances for a resource. If a resource has <see cref="IAuthorizeData"/>
/// then they are evaluated together with the required policy.
/// </remarks>
public AuthorizationPolicy RequiredPolicy { get; set; }
/// <summary>
/// Add an authorization policy with the provided name.
/// </summary>

View File

@ -120,46 +120,74 @@ namespace Microsoft.AspNetCore.Authorization
throw new ArgumentNullException(nameof(authorizeData));
}
var policyBuilder = new AuthorizationPolicyBuilder();
var any = false;
foreach (var authorizeDatum in authorizeData)
// Avoid allocating enumerator if the data is known to be empty
var skipEnumeratingData = false;
if (authorizeData is IList<IAuthorizeData> dataList)
{
any = true;
var useDefaultPolicy = true;
if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy))
skipEnumeratingData = dataList.Count == 0;
}
AuthorizationPolicyBuilder policyBuilder = null;
if (!skipEnumeratingData)
{
foreach (var authorizeDatum in authorizeData)
{
var policy = await policyProvider.GetPolicyAsync(authorizeDatum.Policy);
if (policy == null)
if (policyBuilder == null)
{
throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy));
policyBuilder = new AuthorizationPolicyBuilder();
}
policyBuilder.Combine(policy);
useDefaultPolicy = false;
}
var rolesSplit = authorizeDatum.Roles?.Split(',');
if (rolesSplit != null && rolesSplit.Any())
{
var trimmedRolesSplit = rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim());
policyBuilder.RequireRole(trimmedRolesSplit);
useDefaultPolicy = false;
}
var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(',');
if (authTypesSplit != null && authTypesSplit.Any())
{
foreach (var authType in authTypesSplit)
var useDefaultPolicy = true;
if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy))
{
if (!string.IsNullOrWhiteSpace(authType))
var policy = await policyProvider.GetPolicyAsync(authorizeDatum.Policy);
if (policy == null)
{
policyBuilder.AuthenticationSchemes.Add(authType.Trim());
throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy));
}
policyBuilder.Combine(policy);
useDefaultPolicy = false;
}
var rolesSplit = authorizeDatum.Roles?.Split(',');
if (rolesSplit != null && rolesSplit.Any())
{
var trimmedRolesSplit = rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim());
policyBuilder.RequireRole(trimmedRolesSplit);
useDefaultPolicy = false;
}
var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(',');
if (authTypesSplit != null && authTypesSplit.Any())
{
foreach (var authType in authTypesSplit)
{
if (!string.IsNullOrWhiteSpace(authType))
{
policyBuilder.AuthenticationSchemes.Add(authType.Trim());
}
}
}
}
if (useDefaultPolicy)
{
policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync());
if (useDefaultPolicy)
{
policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync());
}
}
}
return any ? policyBuilder.Build() : null;
var requiredPolicy = await policyProvider.GetRequiredPolicyAsync();
if (requiredPolicy != null)
{
if (policyBuilder == null)
{
policyBuilder = new AuthorizationPolicyBuilder();
}
policyBuilder.Combine(requiredPolicy);
}
return policyBuilder?.Build();
}
}
}

View File

@ -14,6 +14,8 @@ namespace Microsoft.AspNetCore.Authorization
public class DefaultAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
private readonly AuthorizationOptions _options;
private Task<AuthorizationPolicy> _cachedDefaultPolicy;
private Task<AuthorizationPolicy> _cachedRequiredPolicy;
/// <summary>
/// Creates a new instance of <see cref="DefaultAuthorizationPolicyProvider"/>.
@ -35,7 +37,26 @@ namespace Microsoft.AspNetCore.Authorization
/// <returns>The default authorization policy.</returns>
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
return Task.FromResult(_options.DefaultPolicy);
return GetCachedPolicy(ref _cachedDefaultPolicy, _options.DefaultPolicy);
}
/// <summary>
/// Gets the required authorization policy.
/// </summary>
/// <returns>The required authorization policy.</returns>
public Task<AuthorizationPolicy> GetRequiredPolicyAsync()
{
return GetCachedPolicy(ref _cachedRequiredPolicy, _options.RequiredPolicy);
}
private Task<AuthorizationPolicy> GetCachedPolicy(ref Task<AuthorizationPolicy> cachedPolicy, AuthorizationPolicy currentPolicy)
{
var local = cachedPolicy;
if (local == null || local.Result != currentPolicy)
{
cachedPolicy = local = Task.FromResult(currentPolicy);
}
return local;
}
/// <summary>

View File

@ -22,5 +22,11 @@ namespace Microsoft.AspNetCore.Authorization
/// </summary>
/// <returns>The default authorization policy.</returns>
Task<AuthorizationPolicy> GetDefaultPolicyAsync();
/// <summary>
/// Gets the required authorization policy.
/// </summary>
/// <returns>The required authorization policy.</returns>
Task<AuthorizationPolicy> GetRequiredPolicyAsync();
}
}

View File

@ -0,0 +1,65 @@
// 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.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization.Test.TestObjects;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Endpoints;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.AspNetCore.Authorization.Test
{
public class AuthorizationAppBuilderExtensionsTests
{
[Fact]
public async Task UseAuthorization_RegistersMiddleware()
{
// Arrange
var authenticationService = new TestAuthenticationService();
var services = CreateServices(authenticationService);
var app = new ApplicationBuilder(services);
app.UseAuthorization();
var appFunc = app.Build();
var endpoint = new Endpoint(
null,
new EndpointMetadataCollection(new AuthorizeAttribute()),
"Test endpoint");
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = services;
httpContext.SetEndpoint(endpoint);
// Act
await appFunc(httpContext);
// Assert
Assert.True(authenticationService.ChallengeCalled);
}
private IServiceProvider CreateServices(IAuthenticationService authenticationService)
{
var services = new ServiceCollection();
services.AddAuthorization(options => { });
services.AddAuthorizationPolicyEvaluator();
services.AddLogging();
services.AddSingleton(authenticationService);
var serviceProvder = services.BuildServiceProvider();
return serviceProvder;
}
}
}

View File

@ -0,0 +1,63 @@
// 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.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Patterns;
using Xunit;
namespace Microsoft.AspNetCore.Authorization.Test
{
public class AuthorizationEndpointConventionBuilderExtensionsTests
{
[Fact]
public void RequireAuthorization_IAuthorizeData()
{
// Arrange
var builder = new TestEndpointConventionBuilder();
var metadata = new AuthorizeAttribute();
// Act
builder.RequireAuthorization(metadata);
// Assert
var convention = Assert.Single(builder.Conventions);
var endpointModel = new RouteEndpointModel((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0);
convention(endpointModel);
Assert.Equal(metadata, Assert.Single(endpointModel.Metadata));
}
[Fact]
public void RequireAuthorization_PolicyName()
{
// Arrange
var builder = new TestEndpointConventionBuilder();
// Act
builder.RequireAuthorization("policy");
// Assert
var convention = Assert.Single(builder.Conventions);
var endpointModel = new RouteEndpointModel((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0);
convention(endpointModel);
Assert.Equal("policy", Assert.IsAssignableFrom<IAuthorizeData>(Assert.Single(endpointModel.Metadata)).Policy);
}
private class TestEndpointConventionBuilder : IEndpointConventionBuilder
{
public IList<Action<EndpointModel>> Conventions { get; } = new List<Action<EndpointModel>>();
public void Apply(Action<EndpointModel> convention)
{
Conventions.Add(convention);
}
}
}
}

View File

@ -0,0 +1,459 @@
// 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.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Authorization.Test.TestObjects;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Endpoints;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Authorization.Test
{
public class AuthorizationMiddlewareTests
{
[Fact]
public async Task NoEndpoint_AnonymousUser_Allows()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true);
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
}
[Fact]
public async Task NoEndpointWithRequired_AnonymousUser_Challenges()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
}
[Fact]
public async Task HasEndpointWithoutAuth_AnonymousUser_Allows()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint());
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
}
[Fact]
public async Task HasEndpointWithRequiredWithoutAuth_AnonymousUser_Challenges()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint());
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
}
[Fact]
public async Task HasEndpointWithAuth_AnonymousUser_Challenges()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.True(authenticationService.ChallengeCalled);
}
[Fact]
public async Task HasEndpointWithAuth_AnonymousUser_ChallengePerScheme()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().AddAuthenticationSchemes("schema1", "schema2").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.Equal(2, authenticationService.ChallengeCount);
}
[Fact]
public async Task OnAuthorizationAsync_WillCallPolicyProvider()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
var getPolicyCount = 0;
var getRequiredPolicyCount = 0;
policyProvider.Setup(p => p.GetPolicyAsync(It.IsAny<string>())).ReturnsAsync(policy)
.Callback(() => getPolicyCount++);
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy)
.Callback(() => getRequiredPolicyCount++);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute("whatever")));
// Act & Assert
await middleware.Invoke(context);
Assert.Equal(1, getPolicyCount);
Assert.Equal(1, getRequiredPolicyCount);
Assert.Equal(1, next.CalledCount);
await middleware.Invoke(context);
Assert.Equal(2, getPolicyCount);
Assert.Equal(2, getRequiredPolicyCount);
Assert.Equal(2, next.CalledCount);
await middleware.Invoke(context);
Assert.Equal(3, getPolicyCount);
Assert.Equal(3, getRequiredPolicyCount);
Assert.Equal(3, next.CalledCount);
}
[Fact]
public async Task Invoke_ValidClaimShouldNotFail()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireClaim("Permission", "CanViewPage").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()));
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
}
[Fact]
public async Task HasEndpointWithAuthAndAllowAnonymous_AnonymousUser_Allows()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute(), new AllowAnonymousAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
Assert.False(authenticationService.ChallengeCalled);
}
[Fact]
public async Task HasEndpointWithAuth_AuthenticatedUser_Allows()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
Assert.False(authenticationService.ChallengeCalled);
}
[Fact]
public async Task Invoke_AuthSchemesFailShouldSetEmptyPrincipalOnContext()
{
// Arrange
var policy = new AuthorizationPolicyBuilder("Fails").RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.NotNull(context.User?.Identity);
Assert.True(authenticationService.AuthenticateCalled);
Assert.True(authenticationService.ChallengeCalled);
}
[Fact]
public async Task Invoke_SingleValidClaimShouldSucceed()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireClaim("Permission", "CanViewComment", "CanViewPage").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()));
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
}
[Fact]
public async Task AuthZResourceShouldBeEndpoint()
{
// Arrange
object resource = null;
var policy = new AuthorizationPolicyBuilder().RequireAssertion(c =>
{
resource = c.Resource;
return true;
}).Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var endpoint = CreateEndpoint(new AuthorizeAttribute());
var context = GetHttpContext(endpoint: endpoint);
// Act
await middleware.Invoke(context);
// Assert
Assert.Equal(endpoint, resource);
}
[Fact]
public async Task Invoke_RequireUnknownRoleShouldForbid()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireRole("Wut").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.False(authenticationService.ChallengeCalled);
Assert.True(authenticationService.ForbidCalled);
}
[Fact]
public async Task Invoke_RequireUnknownRole_ForbidPerScheme()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireRole("Wut").AddAuthenticationSchemes("Basic", "Bearer").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.Equal(2, authenticationService.ForbidCount);
}
[Fact]
public async Task Invoke_InvalidClaimShouldForbid()
{
// Arrange
var policy = new AuthorizationPolicyBuilder()
.RequireClaim("Permission", "CanViewComment")
.Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.False(authenticationService.ChallengeCalled);
Assert.True(authenticationService.ForbidCalled);
}
private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null)
{
requestDelegate = requestDelegate ?? ((context) => Task.CompletedTask);
return new AuthorizationMiddleware(requestDelegate, policyProvider);
}
private Endpoint CreateEndpoint(params object[] metadata)
{
return new Endpoint(context => Task.CompletedTask, new EndpointMetadataCollection(metadata), "Test endpoint");
}
private HttpContext GetHttpContext(
bool anonymous = false,
Action<IServiceCollection> registerServices = null,
Endpoint endpoint = null,
IAuthenticationService authenticationService = null)
{
var basicPrincipal = new ClaimsPrincipal(
new ClaimsIdentity(
new Claim[] {
new Claim("Permission", "CanViewPage"),
new Claim(ClaimTypes.Role, "Administrator"),
new Claim(ClaimTypes.Role, "User"),
new Claim(ClaimTypes.NameIdentifier, "John")},
"Basic"));
var validUser = basicPrincipal;
var bearerIdentity = new ClaimsIdentity(
new Claim[] {
new Claim("Permission", "CupBearer"),
new Claim(ClaimTypes.Role, "Token"),
new Claim(ClaimTypes.NameIdentifier, "John Bear")},
"Bearer");
validUser.AddIdentity(bearerIdentity);
// ServiceProvider
var serviceCollection = new ServiceCollection();
authenticationService = authenticationService ?? Mock.Of<IAuthenticationService>();
serviceCollection.AddSingleton(authenticationService);
serviceCollection.AddOptions();
serviceCollection.AddLogging();
serviceCollection.AddAuthorization();
serviceCollection.AddAuthorizationPolicyEvaluator();
registerServices?.Invoke(serviceCollection);
var serviceProvider = serviceCollection.BuildServiceProvider();
//// HttpContext
var httpContext = new DefaultHttpContext();
if (endpoint != null)
{
httpContext.SetEndpoint(endpoint);
}
httpContext.RequestServices = serviceProvider;
if (!anonymous)
{
httpContext.User = validUser;
}
return httpContext;
}
private class TestRequestDelegate
{
private readonly int _statusCode;
public bool Called => CalledCount > 0;
public int CalledCount { get; private set; }
public TestRequestDelegate(int statusCode = 200)
{
_statusCode = statusCode;
}
public Task Invoke(HttpContext context)
{
CalledCount++;
context.Response.StatusCode = _statusCode;
return Task.CompletedTask;
}
}
}
}

View File

@ -1025,6 +1025,11 @@ namespace Microsoft.AspNetCore.Authorization.Test
return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
}
public Task<AuthorizationPolicy> GetRequiredPolicyAsync()
{
return Task.FromResult<AuthorizationPolicy>(null);
}
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
@ -1059,6 +1064,11 @@ namespace Microsoft.AspNetCore.Authorization.Test
return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
}
public Task<AuthorizationPolicy> GetRequiredPolicyAsync()
{
return Task.FromResult<AuthorizationPolicy>(null);
}
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
return Task.FromResult(new AuthorizationPolicyBuilder().RequireClaim(policyName).Build());

View File

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
<NoWarn>$(NoWarn);NU1605</NoWarn>
</PropertyGroup>
<ItemGroup>
@ -11,8 +12,10 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(MicrosoftAspNetCoreHttpAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Moq" Version="$(MoqPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,58 @@
// 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.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authorization.Test.TestObjects
{
public class TestAuthenticationService : IAuthenticationService
{
public bool ChallengeCalled => ChallengeCount > 0;
public bool ForbidCalled => ForbidCount > 0;
public bool AuthenticateCalled => AuthenticateCount > 0;
public int ChallengeCount { get; private set; }
public int ForbidCount { get; private set; }
public int AuthenticateCount { get; private set; }
public Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme)
{
AuthenticateCount++;
var identity = context.User.Identities.SingleOrDefault(i => i.AuthenticationType == scheme);
if (identity != null)
{
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(identity), scheme)));
}
return Task.FromResult(AuthenticateResult.Fail("Denied"));
}
public Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties)
{
ChallengeCount++;
return Task.CompletedTask;
}
public Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties)
{
ForbidCount++;
return Task.CompletedTask;
}
public Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
{
throw new NotImplementedException();
}
public Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties)
{
throw new NotImplementedException();
}
}
}