Skip to content

Strawman for new authorization packages that can be shared between server and client #9997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
2 changes: 2 additions & 0 deletions eng/ProjectReferences.props
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" ProjectPath="$(RepositoryRoot)src\Security\Authentication\OpenIdConnect\src\Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj" RefProjectPath="$(RepositoryRoot)src\Security\Authentication\OpenIdConnect\ref\Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Twitter" ProjectPath="$(RepositoryRoot)src\Security\Authentication\Twitter\src\Microsoft.AspNetCore.Authentication.Twitter.csproj" RefProjectPath="$(RepositoryRoot)src\Security\Authentication\Twitter\ref\Microsoft.AspNetCore.Authentication.Twitter.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.WsFederation" ProjectPath="$(RepositoryRoot)src\Security\Authentication\WsFederation\src\Microsoft.AspNetCore.Authentication.WsFederation.csproj" RefProjectPath="$(RepositoryRoot)src\Security\Authentication\WsFederation\ref\Microsoft.AspNetCore.Authentication.WsFederation.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authorization.Common.Abstractions" ProjectPath="$(RepositoryRoot)src\Security\Authorization\Common.Abstractions\src\Microsoft.AspNetCore.Authorization.Common.Abstractions.csproj" RefProjectPath="$(RepositoryRoot)src\Security\Authorization\Common.Abstractions\ref\Microsoft.AspNetCore.Authorization.Common.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authorization.Common" ProjectPath="$(RepositoryRoot)src\Security\Authorization\Common\src\Microsoft.AspNetCore.Authorization.Common.csproj" RefProjectPath="$(RepositoryRoot)src\Security\Authorization\Common\ref\Microsoft.AspNetCore.Authorization.Common.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authorization" ProjectPath="$(RepositoryRoot)src\Security\Authorization\Core\src\Microsoft.AspNetCore.Authorization.csproj" RefProjectPath="$(RepositoryRoot)src\Security\Authorization\Core\ref\Microsoft.AspNetCore.Authorization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authorization.Policy" ProjectPath="$(RepositoryRoot)src\Security\Authorization\Policy\src\Microsoft.AspNetCore.Authorization.Policy.csproj" RefProjectPath="$(RepositoryRoot)src\Security\Authorization\Policy\ref\Microsoft.AspNetCore.Authorization.Policy.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.CookiePolicy" ProjectPath="$(RepositoryRoot)src\Security\CookiePolicy\src\Microsoft.AspNetCore.CookiePolicy.csproj" RefProjectPath="$(RepositoryRoot)src\Security\CookiePolicy\ref\Microsoft.AspNetCore.CookiePolicy.csproj" />
Expand Down
2 changes: 2 additions & 0 deletions eng/SharedFramework.Local.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<!-- These assemblies are available as both a NuGet package and in the shared framework -->
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Http.Features" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Connections.Abstractions" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Authorization.Common.Abstractions" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Authorization.Common" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Http.Connections.Common" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Common" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Microsoft.AspNetCore.Http.HttpResponse</Description>
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Authorization.Common.Abstractions" />
<Reference Include="Microsoft.AspNetCore.Http.Features" />
<Reference Include="Microsoft.Extensions.ActivatorUtilities.Sources" />
<Reference Include="Microsoft.Net.Http.Headers" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsAspNetCoreApp>true</IsAspNetCoreApp>
<IsShippingPackage>true</IsShippingPackage>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@ public virtual void Succeed(IAuthorizationRequirement requirement)
_pendingRequirements.Remove(requirement);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization.Common;

namespace Microsoft.AspNetCore.Authorization
{
Expand Down Expand Up @@ -143,7 +144,8 @@ public static async Task<AuthorizationPolicy> CombineAsync(IAuthorizationPolicyP
var policy = await policyProvider.GetPolicyAsync(authorizeDatum.Policy);
if (policy == null)
{
throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy));
throw new InvalidOperationException(
string.Format(Resources.Exception_AuthorizationPolicyNotFound, authorizeDatum.Policy));
}
policyBuilder.Combine(policy);
useDefaultPolicy = false;
Expand Down Expand Up @@ -179,4 +181,4 @@ public static async Task<AuthorizationPolicy> CombineAsync(IAuthorizationPolicyP
return policyBuilder?.Build();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Extension methods for setting up authorization services in an <see cref="IServiceCollection" />.
/// </summary>
public static class AuthorizationServiceCollectionCommonExtensions
{
/// <summary>
/// Adds authorization services to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddAuthorizationCommon(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}

services.TryAdd(ServiceDescriptor.Transient<IAuthorizationService, DefaultAuthorizationService>());
services.TryAdd(ServiceDescriptor.Transient<IAuthorizationPolicyProvider, DefaultAuthorizationPolicyProvider>());
services.TryAdd(ServiceDescriptor.Transient<IAuthorizationHandlerProvider, DefaultAuthorizationHandlerProvider>());
services.TryAdd(ServiceDescriptor.Transient<IAuthorizationEvaluator, DefaultAuthorizationEvaluator>());
services.TryAdd(ServiceDescriptor.Transient<IAuthorizationHandlerContextFactory, DefaultAuthorizationHandlerContextFactory>());
services.TryAddEnumerable(ServiceDescriptor.Transient<IAuthorizationHandler, PassThroughAuthorizationHandler>());

// Policy
services.TryAdd(ServiceDescriptor.Transient<ICommonPolicyEvaluator, CommonPolicyEvaluator>());

return services;
}

/// <summary>
/// Adds authorization services to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <param name="configure">An action delegate to configure the provided <see cref="AuthorizationOptions"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddAuthorizationCommon(this IServiceCollection services, Action<AuthorizationOptions> configure)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}

if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}

services.Configure(configure);
return services.AddAuthorizationCommon();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,4 @@ public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, obje
return await this.AuthorizeAsync(user, resource, policy);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsAspNetCoreApp>true</IsAspNetCoreApp>
<IsShippingPackage>true</IsShippingPackage>
</PropertyGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Authorization.Common.Abstractions" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="Microsoft.Extensions.Options" />
</ItemGroup>

<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>

<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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.Security.Claims;
using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Authorization.Policy
{
public class CommonPolicyEvaluator : ICommonPolicyEvaluator
Copy link
Member

@HaoK HaoK May 6, 2019

Choose a reason for hiding this comment

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

Do we still need a PolicyEvaluator in the common layer? It was only introduced to tie authN + authZ together, I thought on the client side there's no authentication needed, so wouldn't the base IAuthorizationService be sufficient?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we do want to evaluate policies in client-side code.

I thought on the client side there's no authentication needed

That's not correct. Although authentication isn't enforced there (all true enforcement is server-side), we still want policies to exist as a way of informing the client about what the user is going to be allowed to do.

{
private readonly IAuthorizationService _authorization;

/// <summary>
/// Constructor
/// </summary>
/// <param name="authorization">The authorization service.</param>
public CommonPolicyEvaluator(IAuthorizationService authorization)
{
_authorization = authorization;
}

/// <summary>
/// Attempts authorization for a policy using <see cref="IAuthorizationService"/>.
/// </summary>
/// <param name="policy">The <see cref="AuthorizationPolicy"/>.</param>
/// <param name="authenticationSucceeded">True if authentication succeeded, otherwise false.</param>
/// <param name="user">The <see cref="ClaimsPrincipal"/>.</param>
/// <param name="resource">
/// An optional resource the policy should be checked with.
/// If a resource is not required for policy evaluation you may pass null as the value.
/// </param>
/// <returns>Returns <see cref="PolicyAuthorizationResult.Success"/> if authorization succeeds.
/// Otherwise returns <see cref="PolicyAuthorizationResult.Forbid"/> if <see cref="AuthenticateResult.Succeeded"/>, otherwise
/// returns <see cref="PolicyAuthorizationResult.Challenge"/></returns>
public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, bool authenticationSucceeded, ClaimsPrincipal user, object resource)
{
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}

if (user == null)
{
throw new ArgumentNullException(nameof(user));
}

var result = await _authorization.AuthorizeAsync(user, resource, policy);
if (result.Succeeded)
{
return PolicyAuthorizationResult.Success();
}

// If authentication was successful, return forbidden, otherwise challenge
return authenticationSucceeded
? PolicyAuthorizationResult.Forbid()
: PolicyAuthorizationResult.Challenge();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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.Security.Claims;
using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Authorization.Policy
{
public interface ICommonPolicyEvaluator
{
/// <summary>
/// Attempts authorization for a policy using <see cref="IAuthorizationService"/>.
/// </summary>
/// <param name="policy">The <see cref="AuthorizationPolicy"/>.</param>
/// <param name="authenticationSucceeded">True if authentication succeeded, otherwise false.</param>
/// <param name="user">The <see cref="ClaimsPrincipal"/>.</param>
/// <param name="resource">
/// An optional resource the policy should be checked with.
/// If a resource is not required for policy evaluation you may pass null as the value.
/// </param>
/// <returns>Returns <see cref="PolicyAuthorizationResult.Success"/> if authorization succeeds.
/// Otherwise returns <see cref="PolicyAuthorizationResult.Forbid"/> if <see cref="AuthenticateResult.Succeeded"/>, otherwise
/// returns <see cref="PolicyAuthorizationResult.Challenge"/></returns>
Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, bool authenticationSucceeded, ClaimsPrincipal user, object resource);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ public static PolicyAuthorizationResult Success()
=> new PolicyAuthorizationResult { Succeeded = true };

}
}
}
99 changes: 99 additions & 0 deletions src/Security/Authorization/Common/src/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading