diff --git a/Package.Build.props b/Package.Build.props
index f1be50f..5801518 100644
--- a/Package.Build.props
+++ b/Package.Build.props
@@ -1,6 +1,6 @@
- 0.4.0-alpha
+ 0.5.0-alpha
Hawxy
true
Apache-2.0
diff --git a/build.cmd b/build.cmd
new file mode 100755
index 0000000..b08cc59
--- /dev/null
+++ b/build.cmd
@@ -0,0 +1,7 @@
+:; set -eo pipefail
+:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
+:; ${SCRIPT_DIR}/build.sh "$@"
+:; exit $?
+
+@ECHO OFF
+powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %*
diff --git a/build.ps1 b/build.ps1
index 1c774e5..8c52d63 100644
--- a/build.ps1
+++ b/build.ps1
@@ -63,7 +63,7 @@ else {
$env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
}
-Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
+Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)"
ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet }
ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }
diff --git a/build.sh b/build.sh
index e8961f9..1f3ba09 100755
--- a/build.sh
+++ b/build.sh
@@ -56,7 +56,7 @@ else
export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
fi
-echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)"
+echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)"
"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet
"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"
diff --git a/build/Build.cs b/build/Build.cs
index 2c0800a..d9df69e 100644
--- a/build/Build.cs
+++ b/build/Build.cs
@@ -14,7 +14,6 @@
using static Nuke.Common.IO.PathConstruction;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
-[CheckBuildProjectConfigurations]
[ShutdownDotNetAfterServerBuild]
class Build : NukeBuild
{
diff --git a/build/_build.csproj b/build/_build.csproj
index 32702d7..be8500f 100644
--- a/build/_build.csproj
+++ b/build/_build.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/samples/Fga.Example.AspNetCore/Fga.Example.AspNetCore.csproj b/samples/Fga.Example.AspNetCore/Fga.Example.AspNetCore.csproj
index 9923c00..fa4d9e1 100644
--- a/samples/Fga.Example.AspNetCore/Fga.Example.AspNetCore.csproj
+++ b/samples/Fga.Example.AspNetCore/Fga.Example.AspNetCore.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Fga.Net.AspNetCore/Authorization/FgaCheckDecorator.cs b/src/Fga.Net.AspNetCore/Authorization/FgaCheckDecorator.cs
new file mode 100644
index 0000000..a506864
--- /dev/null
+++ b/src/Fga.Net.AspNetCore/Authorization/FgaCheckDecorator.cs
@@ -0,0 +1,43 @@
+using Auth0.Fga.Api;
+using Auth0.Fga.Model;
+
+namespace Fga.Net.AspNetCore.Authorization;
+
+///
+/// Temporary wrapper to allow for easier testing of middleware. Don't take a dependency on this.
+///
+public class FgaCheckDecorator : IFgaCheckDecorator
+{
+ private readonly Auth0FgaApi _auth0FgaApi;
+
+ ///
+ ///
+ ///
+ ///
+ public FgaCheckDecorator(Auth0FgaApi auth0FgaApi)
+ {
+ _auth0FgaApi = auth0FgaApi;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual Task Check(CheckRequest request, CancellationToken ct) => _auth0FgaApi.Check(request, ct);
+}
+
+///
+/// Temporary wrapper to allow for easier testing of middleware. Don't take a dependency on this.
+///
+public interface IFgaCheckDecorator
+{
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task Check(CheckRequest request, CancellationToken ct);
+}
\ No newline at end of file
diff --git a/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationHandler.cs b/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationHandler.cs
index 45febff..71493a0 100644
--- a/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationHandler.cs
+++ b/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationHandler.cs
@@ -16,24 +16,24 @@ limitations under the License.
*/
#endregion
-using Auth0.Fga.Api;
using Auth0.Fga.Model;
using Fga.Net.AspNetCore.Authorization.Attributes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
namespace Fga.Net.AspNetCore.Authorization;
internal class FineGrainedAuthorizationHandler : AuthorizationHandler
{
- private readonly Auth0FgaApi _client;
+ private readonly IFgaCheckDecorator _client;
+ private readonly ILogger _logger;
- public FineGrainedAuthorizationHandler(Auth0FgaApi client)
+ public FineGrainedAuthorizationHandler(IFgaCheckDecorator client, ILogger logger)
{
_client = client;
+ _logger = logger;
}
-
-
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, FineGrainedAuthorizationRequirement requirement)
{
if (context.Resource is HttpContext httpContext)
@@ -45,7 +45,6 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext
// The user is enforcing the fga policy but there's no attributes here.
if (attributes.Count == 0)
return;
- var results = new List();
foreach (var attribute in attributes)
{
var user = await attribute.GetUser(httpContext);
@@ -55,7 +54,7 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(relation) || string.IsNullOrEmpty(@object))
return;
- var result = await _client.Check(new CheckRequestParams()
+ var result = await _client.Check(new CheckRequest()
{
TupleKey = new TupleKey
{
@@ -65,13 +64,13 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext
}
}, httpContext.RequestAborted);
- results.Add(result.Allowed);
+ if (!result.Allowed)
+ {
+ _logger.CheckFailureDebug(user, relation, @object);
+ return;
+ }
}
-
- if(results.All(x => x))
- context.Succeed(requirement);
+ context.Succeed(requirement);
}
}
}
-
-
diff --git a/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationRequirement.cs b/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationRequirement.cs
index 81c8b81..fa679f5 100644
--- a/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationRequirement.cs
+++ b/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationRequirement.cs
@@ -22,4 +22,7 @@ namespace Fga.Net.AspNetCore.Authorization;
internal class FineGrainedAuthorizationRequirement : IAuthorizationRequirement
{
+ public override string ToString() =>
+ $"{nameof(FineGrainedAuthorizationRequirement)}: Requires FGA Authorization checks to pass.";
+
}
\ No newline at end of file
diff --git a/src/Fga.Net.AspNetCore/Authorization/Log.cs b/src/Fga.Net.AspNetCore/Authorization/Log.cs
new file mode 100644
index 0000000..03aa4dc
--- /dev/null
+++ b/src/Fga.Net.AspNetCore/Authorization/Log.cs
@@ -0,0 +1,27 @@
+#region License
+/*
+ Copyright 2021-2022 Hawxy
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+#endregion
+
+using Microsoft.Extensions.Logging;
+
+namespace Fga.Net.AspNetCore.Authorization;
+
+internal static partial class Log
+{
+ [LoggerMessage(0, LogLevel.Debug, "FGA Check failed for User: {user}, Relation: {relation}, Object: {object}")]
+ public static partial void CheckFailureDebug(this ILogger logger, string user, string relation, string @object);
+}
\ No newline at end of file
diff --git a/src/Fga.Net.AspNetCore/Controllers/FgaControllerBase.cs b/src/Fga.Net.AspNetCore/Controllers/FgaControllerBase.cs
index 4048152..dd43c12 100644
--- a/src/Fga.Net.AspNetCore/Controllers/FgaControllerBase.cs
+++ b/src/Fga.Net.AspNetCore/Controllers/FgaControllerBase.cs
@@ -47,7 +47,7 @@ public FgaControllerBase(Auth0FgaApi client)
///
public async Task Check(string user, string relation, string @object, CancellationToken ct)
{
- var checkRes = await _client.Check(new CheckRequestParams()
+ var checkRes = await _client.Check(new CheckRequest()
{
TupleKey = new TupleKey
{
diff --git a/src/Fga.Net.AspNetCore/ServiceCollectionExtensions.cs b/src/Fga.Net.AspNetCore/ServiceCollectionExtensions.cs
index 14ac98c..2e9d6a9 100644
--- a/src/Fga.Net.AspNetCore/ServiceCollectionExtensions.cs
+++ b/src/Fga.Net.AspNetCore/ServiceCollectionExtensions.cs
@@ -40,6 +40,7 @@ public static IServiceCollection AddAuth0Fga(this IServiceCollection collection,
ArgumentNullException.ThrowIfNull(config);
collection.AddAuth0FgaClient(config);
+ collection.AddScoped();
collection.AddScoped();
return collection;
}
diff --git a/src/Fga.Net/Fga.Net.DependencyInjection.csproj b/src/Fga.Net/Fga.Net.DependencyInjection.csproj
index 6e5bcd3..1a922af 100644
--- a/src/Fga.Net/Fga.Net.DependencyInjection.csproj
+++ b/src/Fga.Net/Fga.Net.DependencyInjection.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/tests/Fga.Net.Tests/Client/EndpointTests.cs b/tests/Fga.Net.Tests/Client/EndpointTests.cs
index 1bff1f2..4f29381 100644
--- a/tests/Fga.Net.Tests/Client/EndpointTests.cs
+++ b/tests/Fga.Net.Tests/Client/EndpointTests.cs
@@ -23,13 +23,19 @@ private async Task GetEndpoints_Return_200()
{
using var scope = _host.Services.CreateScope();
var client = scope.ServiceProvider.GetRequiredService();
- var modelIds = await client.ReadAuthorizationModels();
+ var modelsResponse = await client.ReadAuthorizationModels();
- Assert.NotNull(modelIds);
- Assert.NotNull(modelIds.AuthorizationModelIds);
- Assert.True(modelIds.AuthorizationModelIds?.Count > 0);
+ Assert.NotNull(modelsResponse);
+ Assert.NotNull(modelsResponse.AuthorizationModels);
+ Assert.True(modelsResponse.AuthorizationModels?.Count > 0);
+
+ var modelId = modelsResponse.AuthorizationModels?.First().Id!;
+
+ var modelResponse = await client.ReadAuthorizationModel(modelId);
+
+ Assert.NotNull(modelResponse);
+ Assert.NotNull(modelResponse.AuthorizationModel.Id);
- var modelId = modelIds.AuthorizationModelIds?.First()!;
var assertions = await client.ReadAssertions(modelId);
Assert.NotNull(assertions);
@@ -40,7 +46,7 @@ private async Task GetEndpoints_Return_200()
Assert.NotEmpty(assertion.Relation!);
Assert.NotEmpty(assertion.User!);
- var graph = await client.Expand(new ExpandRequestParams()
+ var graph = await client.Expand(new ExpandRequest()
{
AuthorizationModelId = modelId,
TupleKey = assertion
@@ -51,7 +57,6 @@ private async Task GetEndpoints_Return_200()
var watch = await client.ReadChanges();
Assert.NotNull(watch);
-
}
diff --git a/tests/Fga.Net.Tests/Fga.Net.Tests.csproj b/tests/Fga.Net.Tests/Fga.Net.Tests.csproj
index 99c61f2..cecc254 100644
--- a/tests/Fga.Net.Tests/Fga.Net.Tests.csproj
+++ b/tests/Fga.Net.Tests/Fga.Net.Tests.csproj
@@ -9,10 +9,10 @@
-
-
+
+
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/tests/Fga.Net.Tests/Middleware/MiddlewareTests.cs b/tests/Fga.Net.Tests/Middleware/MiddlewareTests.cs
index 54ad1cd..73c1d0e 100644
--- a/tests/Fga.Net.Tests/Middleware/MiddlewareTests.cs
+++ b/tests/Fga.Net.Tests/Middleware/MiddlewareTests.cs
@@ -19,7 +19,7 @@ public MiddlewareTests(WebAppFixture fixture)
{
_alba = fixture.AlbaHost;
}
- [Fact(Skip="Moq is broke")]
+ [Fact]
public async Task Authorization_HappyPath_Succeeds()
{
await _alba.Scenario(_ =>
@@ -28,7 +28,7 @@ await _alba.Scenario(_ =>
_.StatusCodeShouldBeOk();
});
}
- [Fact(Skip = "Moq is broke")]
+ [Fact]
public async Task Authorization_UnhappyPath_Forbidden()
{
await _alba.Scenario(_ =>
diff --git a/tests/Fga.Net.Tests/Middleware/WebAppFixture.cs b/tests/Fga.Net.Tests/Middleware/WebAppFixture.cs
index bd9530a..4f0f480 100644
--- a/tests/Fga.Net.Tests/Middleware/WebAppFixture.cs
+++ b/tests/Fga.Net.Tests/Middleware/WebAppFixture.cs
@@ -1,8 +1,8 @@
using System.Threading;
using System.Threading.Tasks;
using Alba;
-using Auth0.Fga.Api;
using Auth0.Fga.Model;
+using Fga.Net.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Moq;
@@ -16,12 +16,12 @@ public class WebAppFixture : IAsyncLifetime
public async Task InitializeAsync()
{
- var authorizationClientMock = new Mock();
+ var authorizationClientMock = new Mock();
authorizationClientMock.Setup(c =>
- c.Check(It.IsAny(),
+ c.Check(It.IsAny(),
It.IsAny()))
- .ReturnsAsync((string _, CheckRequestParams res, CancellationToken _) =>
+ .ReturnsAsync((CheckRequest res, CancellationToken _) =>
res.TupleKey!.User == MockJwtConfiguration.DefaultUser
? new CheckResponse() { Allowed = true }
: new CheckResponse() { Allowed = false });
@@ -31,7 +31,7 @@ public async Task InitializeAsync()
{
builder.ConfigureServices(s =>
{
- s.Replace(ServiceDescriptor.Scoped(_ => authorizationClientMock.Object));
+ s.Replace(ServiceDescriptor.Scoped(_ => authorizationClientMock.Object));
});
}, MockJwtConfiguration.GetDefaultStubConfiguration());