-
-
Notifications
You must be signed in to change notification settings - Fork 748
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
44 changed files
with
1,962 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
...rc/AspNetCore.Authorization.Opa/HotChocolate - Backup.AspNetCore.Authorization.Opa.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="System.Text.Json" Version="6.0.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
14 changes: 14 additions & 0 deletions
14
...NetCore/src/AspNetCore.Authorization.Opa/HotChocolate.AspNetCore.Authorization.Opa.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\..\Core\src\Types\HotChocolate.Types.csproj" /> | ||
<ProjectReference Include="..\AspNetCore.Authorization\HotChocolate.AspNetCore.Authorization.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
122 changes: 122 additions & 0 deletions
122
src/HotChocolate/AspNetCore/src/AspNetCore.Authorization.Opa/OpaAuthorizationHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#if NET6_0 | ||
using System.Net.Http.Json; | ||
#endif | ||
using System.Text.Json; | ||
using HotChocolate.Resolvers; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace HotChocolate.AspNetCore.Authorization; | ||
|
||
public sealed class QueryResponse | ||
{ | ||
public Guid? DecisionId { get; set; } | ||
public bool Result { get; set; } | ||
} | ||
|
||
public sealed class QueryRequest | ||
{ | ||
public Input Input { get; set; } = Input.Empty; | ||
} | ||
|
||
internal static class OpaHttpExtensions | ||
{ | ||
internal static HttpContent ToJsonContent(this QueryRequest request, JsonSerializerOptions options) | ||
{ | ||
#if NET6_0 | ||
return JsonContent.Create(request, options: options); | ||
#else | ||
var body = JsonSerializer.Serialize(request, options); | ||
return new StringContent(body, System.Text.Encoding.UTF8, "application/json"); | ||
#endif | ||
} | ||
|
||
internal static async Task<QueryResponse?> QueryResponseFromJsonAsync(this HttpContent content, JsonSerializerOptions options, CancellationToken token) | ||
{ | ||
#if NET6_0 | ||
return await content.ReadFromJsonAsync<QueryResponse>(options, token); | ||
#else | ||
return await JsonSerializer.DeserializeAsync<QueryResponse>(await content.ReadAsStreamAsync(), options, token); | ||
#endif | ||
} | ||
} | ||
|
||
public sealed class Input | ||
{ | ||
public static readonly Input Empty = new(); | ||
} | ||
|
||
public interface IOpaService | ||
{ | ||
Task<QueryResponse?> QueryAsync(string policyPath, QueryRequest request, CancellationToken token); | ||
} | ||
|
||
public sealed class OpaOptions | ||
{ | ||
public JsonSerializerOptions JsonSerializerOptions { get; set; } = new JsonSerializerOptions(); | ||
} | ||
|
||
public sealed class OpaService : IOpaService | ||
{ | ||
private readonly HttpClient _httpClient; | ||
private readonly OpaOptions _options; | ||
|
||
public OpaService(HttpClient httpClient, OpaOptions options) | ||
{ | ||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); | ||
_options = options ?? throw new ArgumentNullException(nameof(options)); | ||
} | ||
|
||
public async Task<QueryResponse?> QueryAsync(string policyPath, QueryRequest request, CancellationToken token) | ||
{ | ||
if (policyPath is null) throw new ArgumentNullException(nameof(policyPath)); | ||
if (request is null) throw new ArgumentNullException(nameof(request)); | ||
|
||
HttpResponseMessage? response = await _httpClient.PostAsync(policyPath, request.ToJsonContent(_options.JsonSerializerOptions), token); | ||
response.EnsureSuccessStatusCode(); | ||
return await response.Content.QueryResponseFromJsonAsync(_options.JsonSerializerOptions, token); | ||
} | ||
} | ||
|
||
|
||
public interface IOpaDecision | ||
{ | ||
AuthorizeResult Map(QueryResponse? response); | ||
} | ||
|
||
public sealed class OpaDecision : IOpaDecision | ||
{ | ||
public AuthorizeResult Map(QueryResponse? response) | ||
{ | ||
if (response is not null && response.Result) | ||
{ | ||
return AuthorizeResult.Allowed; | ||
} | ||
return AuthorizeResult.NotAllowed; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// An implementation that delegates authz to OPA (Open Policy Agent) REST API endpoint | ||
/// </summary> | ||
public class OpaAuthorizationHandler : IAuthorizationHandler | ||
{ | ||
/// <summary> | ||
/// Authorize current directive using Microsoft.AspNetCore.Authorization. | ||
/// </summary> | ||
/// <param name="context">The current middleware context.</param> | ||
/// <param name="directive">The authorization directive.</param> | ||
/// <returns> | ||
/// Returns a value indicating if the current session is authorized to | ||
/// access the resolver data. | ||
/// </returns> | ||
public async ValueTask<AuthorizeResult> AuthorizeAsync( | ||
IMiddlewareContext context, | ||
AuthorizeDirective directive) | ||
{ | ||
IOpaService? opaService = context.Services.GetRequiredService<IOpaService>(); | ||
IOpaDecision? opaDecision = context.Services.GetRequiredService<IOpaDecision>(); | ||
var request = new QueryRequest { Input = new Input()}; | ||
QueryResponse? response = await opaService.QueryAsync(directive.Policy ?? string.Empty, request, context.RequestAborted); | ||
return opaDecision.Map(response); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
...late/AspNetCore/test/AspNetCore.Authorization.Opa.Tests/AuthorizationAttributeTestData.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using HotChocolate.Execution.Configuration; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace HotChocolate.AspNetCore.Authorization; | ||
|
||
public class AuthorizationAttributeTestData : IEnumerable<object[]> | ||
{ | ||
public class Query | ||
{ | ||
[Authorize] | ||
public string GetDefault() => "foo"; | ||
|
||
[Authorize(Policy = "HasAgeDefined")] | ||
public string GetAge() => "foo"; | ||
|
||
[Authorize(Roles = new[] { "a" })] | ||
public string GetRoles() => "foo"; | ||
|
||
[Authorize(Roles = new[] { "a", "b" })] | ||
[GraphQLName("roles_ab")] | ||
public string GetRolesAb() => "foo"; | ||
|
||
[Authorize(Policy = "a")] | ||
[Authorize(Policy = "b")] | ||
public string GetPiped() => "foo"; | ||
|
||
[Authorize(Policy = "a", Apply = ApplyPolicy.AfterResolver)] | ||
public string GetAfterResolver() => "foo"; | ||
} | ||
|
||
private Action<IRequestExecutorBuilder> CreateSchema() => | ||
builder => builder | ||
.AddQueryType<Query>() | ||
.AddAuthorization(); | ||
|
||
public IEnumerator<object[]> GetEnumerator() | ||
{ | ||
yield return new object[] { CreateSchema() }; | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
} |
45 changes: 45 additions & 0 deletions
45
...Chocolate/AspNetCore/test/AspNetCore.Authorization.Opa.Tests/AuthorizationHandlerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using System.Threading.Tasks; | ||
using HotChocolate.Execution; | ||
using HotChocolate.Resolvers; | ||
using HotChocolate.Tests; | ||
using HotChocolate.Types; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Snapshooter; | ||
using Snapshooter.Xunit; | ||
using Xunit; | ||
|
||
namespace HotChocolate.AspNetCore.Authorization; | ||
|
||
public class AuthorizationHandlerTests | ||
{ | ||
[InlineData(AuthorizeResult.Allowed)] | ||
[InlineData(AuthorizeResult.NoDefaultPolicy)] | ||
[InlineData(AuthorizeResult.NotAllowed)] | ||
[InlineData(AuthorizeResult.NotAuthenticated)] | ||
[InlineData(AuthorizeResult.PolicyNotFound)] | ||
[Theory] | ||
public async Task Authorize(AuthorizeResult result) | ||
{ | ||
Snapshot.FullName(new SnapshotNameExtension(result)); | ||
|
||
await new ServiceCollection() | ||
.AddGraphQLServer() | ||
.AddQueryType() | ||
.AddTypeExtension<QueryExtensions>() | ||
.AddAuthorizationHandler<OpaAuthorizationHandler>() | ||
.ExecuteRequestAsync( | ||
QueryRequestBuilder | ||
.New() | ||
.SetQuery("{ bar }") | ||
.AddProperty("auth", result) | ||
.Create()) | ||
.MatchSnapshotAsync(); | ||
} | ||
|
||
[Authorize] | ||
[ExtendObjectType(OperationTypeNames.Query)] | ||
public class QueryExtensions | ||
{ | ||
public string Bar() => "bar"; | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Opa.Tests/AuthorizationTestData.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using HotChocolate.Execution.Configuration; | ||
using HotChocolate.Resolvers; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace HotChocolate.AspNetCore.Authorization; | ||
|
||
public class AuthorizationTestData : IEnumerable<object[]> | ||
{ | ||
private readonly string SchemaCode = @" | ||
type Query { | ||
default: String @authorize | ||
age: String @authorize(policy: ""HasAgeDefined"") | ||
roles: String @authorize(roles: [""a""]) | ||
roles_ab: String @authorize(roles: [""a"" ""b""]) | ||
piped: String | ||
@authorize(policy: ""a"") | ||
@authorize(policy: ""b"") | ||
afterResolver: String | ||
@authorize(policy: ""a"" apply: AFTER_RESOLVER) | ||
} | ||
"; | ||
|
||
private readonly FieldMiddleware _schemaMiddleware = next => context => | ||
{ | ||
context.Result = "foo"; | ||
return next.Invoke(context); | ||
}; | ||
|
||
private Action<IRequestExecutorBuilder> CreateSchema() => | ||
sb => sb | ||
.AddDocumentFromString(SchemaCode) | ||
.AddAuthorization() | ||
.UseField(_schemaMiddleware); | ||
|
||
private Action<IRequestExecutorBuilder> CreateSchemaWithBuilder() => | ||
sb => sb | ||
.AddDocumentFromString(SchemaCode) | ||
.AddAuthorization() | ||
.UseField(_schemaMiddleware); | ||
|
||
public IEnumerator<object[]> GetEnumerator() | ||
{ | ||
yield return new object[] { CreateSchema() }; | ||
yield return new object[] { CreateSchemaWithBuilder() }; | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
} |
Oops, something went wrong.