diff --git a/.github/workflows/Build_&_Test.yml b/.github/workflows/Build_&_Test.yml
index f60b2db..256c10b 100644
--- a/.github/workflows/Build_&_Test.yml
+++ b/.github/workflows/Build_&_Test.yml
@@ -29,9 +29,12 @@ jobs:
name: ubuntu-latest
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '9'
- name: 'Cache: .nuke/temp, ~/.nuget/packages'
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: |
.nuke/temp
diff --git a/.github/workflows/Manual_Nuget_Push.yml b/.github/workflows/Manual_Nuget_Push.yml
index 799fe86..4299d2a 100644
--- a/.github/workflows/Manual_Nuget_Push.yml
+++ b/.github/workflows/Manual_Nuget_Push.yml
@@ -23,9 +23,12 @@ jobs:
name: ubuntu-latest
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '9'
- name: 'Cache: .nuke/temp, ~/.nuget/packages'
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: |
.nuke/temp
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index 68c5085..412ee68 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -1,64 +1,65 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
- "$ref": "#/definitions/build",
- "title": "Build Schema",
"definitions": {
- "build": {
- "type": "object",
+ "Host": {
+ "type": "string",
+ "enum": [
+ "AppVeyor",
+ "AzurePipelines",
+ "Bamboo",
+ "Bitbucket",
+ "Bitrise",
+ "GitHubActions",
+ "GitLab",
+ "Jenkins",
+ "Rider",
+ "SpaceAutomation",
+ "TeamCity",
+ "Terminal",
+ "TravisCI",
+ "VisualStudio",
+ "VSCode"
+ ]
+ },
+ "ExecutableTarget": {
+ "type": "string",
+ "enum": [
+ "Clean",
+ "Compile",
+ "NugetPack",
+ "NugetPush",
+ "Restore",
+ "Test"
+ ]
+ },
+ "Verbosity": {
+ "type": "string",
+ "description": "",
+ "enum": [
+ "Verbose",
+ "Normal",
+ "Minimal",
+ "Quiet"
+ ]
+ },
+ "NukeBuild": {
"properties": {
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
},
- "FgaClientId": {
- "type": "string",
- "description": "FGA Client ID",
- "default": "Secrets must be entered via 'nuke :secrets [profile]'"
- },
- "FgaClientSecret": {
- "type": "string",
- "description": "FGA Client Secret",
- "default": "Secrets must be entered via 'nuke :secrets [profile]'"
- },
- "FgaStoreId": {
- "type": "string",
- "description": "FGA Store ID",
- "default": "Secrets must be entered via 'nuke :secrets [profile]'"
- },
"Help": {
"type": "boolean",
"description": "Shows the help text for this build assembly"
},
"Host": {
- "type": "string",
"description": "Host for execution. Default is 'automatic'",
- "enum": [
- "AppVeyor",
- "AzurePipelines",
- "Bamboo",
- "Bitbucket",
- "Bitrise",
- "GitHubActions",
- "GitLab",
- "Jenkins",
- "Rider",
- "SpaceAutomation",
- "TeamCity",
- "Terminal",
- "TravisCI",
- "VisualStudio",
- "VSCode"
- ]
+ "$ref": "#/definitions/Host"
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
- "NugetApiKey": {
- "type": "string",
- "description": "Nuget Api Key",
- "default": "Secrets must be entered via 'nuke :secrets [profile]'"
- },
"Partition": {
"type": "string",
"description": "Partition to use on CI"
@@ -82,47 +83,54 @@
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
- "type": "string",
- "enum": [
- "Clean",
- "Compile",
- "NugetPack",
- "NugetPush",
- "Restore",
- "Test"
- ]
+ "$ref": "#/definitions/ExecutableTarget"
}
},
- "Solution": {
- "type": "string",
- "description": "Path to a solution file that is automatically loaded"
- },
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
- "type": "string",
- "enum": [
- "Clean",
- "Compile",
- "NugetPack",
- "NugetPush",
- "Restore",
- "Test"
- ]
+ "$ref": "#/definitions/ExecutableTarget"
}
},
"Verbosity": {
- "type": "string",
"description": "Logging verbosity during build execution. Default is 'Normal'",
- "enum": [
- "Minimal",
- "Normal",
- "Quiet",
- "Verbose"
- ]
+ "$ref": "#/definitions/Verbosity"
+ }
+ }
+ }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "FgaClientId": {
+ "type": "string",
+ "description": "FGA Client ID",
+ "default": "Secrets must be entered via 'nuke :secrets [profile]'"
+ },
+ "FgaClientSecret": {
+ "type": "string",
+ "description": "FGA Client Secret",
+ "default": "Secrets must be entered via 'nuke :secrets [profile]'"
+ },
+ "FgaStoreId": {
+ "type": "string",
+ "description": "FGA Store ID",
+ "default": "Secrets must be entered via 'nuke :secrets [profile]'"
+ },
+ "NugetApiKey": {
+ "type": "string",
+ "description": "Nuget Api Key",
+ "default": "Secrets must be entered via 'nuke :secrets [profile]'"
+ },
+ "Solution": {
+ "type": "string",
+ "description": "Path to a solution file that is automatically loaded"
}
}
+ },
+ {
+ "$ref": "#/definitions/NukeBuild"
}
- }
+ ]
}
diff --git a/Package.Build.props b/Package.Build.props
index d6bf39f..765873a 100644
--- a/Package.Build.props
+++ b/Package.Build.props
@@ -1,6 +1,6 @@
- 1.2.0
+ 2.0.0-RC.1
Hawxy
true
Apache-2.0
diff --git a/README.md b/README.md
index 066d1e1..a1b62c9 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
## Getting Started
-This package is compatible with the OSS OpenFGA as well as the managed Auth0 FGA service. Usage of DSL v1.1 is required.
+This package is compatible with the OSS OpenFGA as well as the managed Auth0 FGA service.
Please ensure you have a basic understanding of how FGA works before continuing: [OpenFGA Docs](https://openfga.dev/) or [Auth0 FGA Docs](https://docs.fga.dev/)
@@ -174,6 +174,10 @@ public class ComputedRelationshipAttribute : FgaBaseObjectAttribute
An additional pre-made attribute that allows all tuple values to be hardcoded strings ships with the package (`FgaStringAttribute`). This attribute is useful for testing and debug purposes, but should not be used in a real application.
+### Contextual Tuples
+
+All attributes supports specifying contextual tuples as part of a check. Inherit & override `GetContextualTuple` to provide the relevant logic in your own attribute.
+
## Client Injection
This package registers both the `OpenFgaApi` and `OpenFgaClient` types in the DI container. `OpenFgaClient` is a higher level abstraction and preferred over `OpenFgaApi` for general use.
diff --git a/build/Build.cs b/build/Build.cs
index 4b04d6c..977000b 100644
--- a/build/Build.cs
+++ b/build/Build.cs
@@ -13,6 +13,7 @@
[GitHubActions(
"Build & Test",
GitHubActionsImage.UbuntuLatest,
+ AutoGenerate = false,
OnPushBranches = new []{ "main" },
OnPullRequestBranches = new []{ "main" },
InvokedTargets = new[] { nameof(Test) },
@@ -20,6 +21,7 @@
[GitHubActions(
"Manual Nuget Push",
GitHubActionsImage.UbuntuLatest,
+ AutoGenerate = false,
On = new[] { GitHubActionsTrigger.WorkflowDispatch },
InvokedTargets = new[] { nameof(NugetPush) },
ImportSecrets = new[] { nameof(NugetApiKey) })]
diff --git a/build/_build.csproj b/build/_build.csproj
index c72030f..446c9fd 100644
--- a/build/_build.csproj
+++ b/build/_build.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/samples/Fga.Example.AspNetCore/ComputedRelationshipAttribute.cs b/samples/Fga.Example.AspNetCore/ComputedRelationshipAttribute.cs
index 5349d06..3a1fca2 100644
--- a/samples/Fga.Example.AspNetCore/ComputedRelationshipAttribute.cs
+++ b/samples/Fga.Example.AspNetCore/ComputedRelationshipAttribute.cs
@@ -1,4 +1,5 @@
using Fga.Net.AspNetCore.Authorization.Attributes;
+using OpenFga.Sdk.Client.Model;
namespace Fga.Example.AspNetCore;
diff --git a/src/Fga.Net.AspNetCore/Authorization/Attributes/FgaAttribute.cs b/src/Fga.Net.AspNetCore/Authorization/Attributes/FgaAttribute.cs
index 054b3a7..a43075d 100644
--- a/src/Fga.Net.AspNetCore/Authorization/Attributes/FgaAttribute.cs
+++ b/src/Fga.Net.AspNetCore/Authorization/Attributes/FgaAttribute.cs
@@ -17,6 +17,8 @@ limitations under the License.
#endregion
using Microsoft.AspNetCore.Http;
+using OpenFga.Sdk.Client.Model;
+using Tuple = OpenFga.Sdk.Model.Tuple;
namespace Fga.Net.AspNetCore.Authorization.Attributes;
@@ -42,8 +44,15 @@ public abstract class FgaAttribute : Attribute
/// An entity in the system.
///
/// The context of the current request
- /// Usually a string in an entity-identifier format: document:id
+ /// Usually a string in an entity-identifier format: document:id
public abstract ValueTask GetObject(HttpContext context);
+
+ ///
+ /// Contextual tuple(s) to apply the check generated from this attribute.
+ ///
+ /// The context of the current request
+ /// The list of contextual tuples, or null if none were provided
+ public virtual ValueTask?> GetContextualTuple(HttpContext context) => new((List?)null);
///
/// Concats the type and identifier into the object format
@@ -51,6 +60,6 @@ public abstract class FgaAttribute : Attribute
/// The objects type, such as workspace, repository, organization or document
/// The objects identifier
/// The object in the entity:identifier format
- public static string FormatObject(string type, string identifier) => $"{type}:{identifier}";
+ protected static string FormatObject(string type, string identifier) => $"{type}:{identifier}";
}
diff --git a/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationHandler.cs b/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationHandler.cs
index e7b228a..2141d88 100644
--- a/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationHandler.cs
+++ b/src/Fga.Net.AspNetCore/Authorization/FineGrainedAuthorizationHandler.cs
@@ -48,7 +48,7 @@ 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 checks = new List();
foreach (var attribute in attributes)
@@ -56,11 +56,13 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext
string? user;
string? relation;
string? @object;
+ List? contextualTuples;
try
{
user = await attribute.GetUser(httpContext);
relation = await attribute.GetRelation(httpContext);
@object = await attribute.GetObject(httpContext);
+ contextualTuples = await attribute.GetContextualTuple(httpContext);
}
catch (FgaMiddlewareException ex)
{
@@ -85,7 +87,8 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext
{
User = user,
Relation = relation,
- Object = @object
+ Object = @object,
+ ContextualTuples = contextualTuples
});
}
diff --git a/src/Fga.Net.AspNetCore/Authorization/Log.cs b/src/Fga.Net.AspNetCore/Authorization/Log.cs
index 9a7f24a..2ff58b4 100644
--- a/src/Fga.Net.AspNetCore/Authorization/Log.cs
+++ b/src/Fga.Net.AspNetCore/Authorization/Log.cs
@@ -37,7 +37,5 @@ internal static partial class Log
[LoggerMessage(3005, LogLevel.Debug, "User was not in a valid format of 'type:id' or '*'. Computed user as '{user}'")]
public static partial void InvalidUser(this ILogger logger, string user);
-
-
}
\ No newline at end of file
diff --git a/src/Fga.Net.AspNetCore/Fga.Net.AspNetCore.csproj b/src/Fga.Net.AspNetCore/Fga.Net.AspNetCore.csproj
index 0e35314..798b330 100644
--- a/src/Fga.Net.AspNetCore/Fga.Net.AspNetCore.csproj
+++ b/src/Fga.Net.AspNetCore/Fga.Net.AspNetCore.csproj
@@ -1,7 +1,7 @@
- net6.0;net7.0;net8.0
+ net8.0;net9.0
enable
enable
@@ -17,7 +17,7 @@
-
+
diff --git a/src/Fga.Net/Configuration/Auth0FgaConnectionBuilder.cs b/src/Fga.Net/Configuration/Auth0FgaConnectionBuilder.cs
index 6f81c66..7f4a728 100644
--- a/src/Fga.Net/Configuration/Auth0FgaConnectionBuilder.cs
+++ b/src/Fga.Net/Configuration/Auth0FgaConnectionBuilder.cs
@@ -48,10 +48,10 @@ internal sealed record Auth0FgaEnvironment(string ApiHost, string ApiTokenIssuer
///
public sealed class Auth0FgaConnectionBuilder
{
- private const string FgaIssuer = "fga.us.auth0.com";
+ private const string FgaIssuer = "auth.fga.dev";
- private readonly IReadOnlyDictionary _fgaEnvironments =
- new Dictionary()
+ private readonly Dictionary _fgaEnvironments =
+ new()
{
{
FgaEnvironment.US,
diff --git a/src/Fga.Net/Configuration/OpenFgaConnectionBuilder.cs b/src/Fga.Net/Configuration/OpenFgaConnectionBuilder.cs
index 58b9170..1fd0c73 100644
--- a/src/Fga.Net/Configuration/OpenFgaConnectionBuilder.cs
+++ b/src/Fga.Net/Configuration/OpenFgaConnectionBuilder.cs
@@ -26,20 +26,7 @@ namespace Fga.Net.DependencyInjection.Configuration;
public sealed class OpenFgaConnectionBuilder
{
private string? _apiUrl;
-
- ///
- /// Sets the connection configuration for the host.
- ///
- /// API scheme, either http or https.
- /// API host, should be in be plain URI format
- ///
- [Obsolete("Passing in a split scheme & host is obsolete and will be removed in a future release. Use SetConnection(string apiUrl)")]
- public OpenFgaConnectionBuilder SetConnection(string apiScheme, string apiHost)
- {
- _apiUrl = $"{apiScheme}://{apiHost}";
- return this;
- }
-
+
///
/// Sets the connection configuration for the host.
///
diff --git a/src/Fga.Net/Fga.Net.DependencyInjection.csproj b/src/Fga.Net/Fga.Net.DependencyInjection.csproj
index 2c35f73..b7a6ee7 100644
--- a/src/Fga.Net/Fga.Net.DependencyInjection.csproj
+++ b/src/Fga.Net/Fga.Net.DependencyInjection.csproj
@@ -1,6 +1,6 @@
- net6.0;net8.0
+ net8.0;net9.0
enable
enable
@@ -12,17 +12,17 @@
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Fga.Net/FgaConfigurationBuilder.cs b/src/Fga.Net/FgaConfigurationBuilder.cs
index f7f6815..50f546d 100644
--- a/src/Fga.Net/FgaConfigurationBuilder.cs
+++ b/src/Fga.Net/FgaConfigurationBuilder.cs
@@ -18,6 +18,7 @@ limitations under the License.
using Fga.Net.DependencyInjection.Configuration;
using OpenFga.Sdk.Client;
+using OpenFga.Sdk.Configuration;
namespace Fga.Net.DependencyInjection;
@@ -62,7 +63,15 @@ public FgaConfigurationBuilder SetWaitInMs(int waitInMs)
_minWaitInMs = waitInMs;
return this;
}
+
+ private TelemetryConfig? _telemetryConfig;
+ ///
+ public FgaConfigurationBuilder SetTelemetry(TelemetryConfig config)
+ {
+ _telemetryConfig = config;
+ return this;
+ }
///
/// Configures the client for use with OpenFga
@@ -90,6 +99,7 @@ public void ConfigureAuth0Fga(Action config)
config.Invoke(configuration);
_fgaConfiguration = configuration.Build();
}
+
internal FgaBuiltConfiguration Build()
{
if (_fgaConfiguration is null)
@@ -97,6 +107,6 @@ internal FgaBuiltConfiguration Build()
if (string.IsNullOrEmpty(_storeId))
throw new InvalidOperationException("Store ID must be set");
- return new FgaBuiltConfiguration(_storeId, _authorizationModelId, _maxRetry, _minWaitInMs, _fgaConfiguration);
+ return new FgaBuiltConfiguration(_storeId, _authorizationModelId, _maxRetry, _minWaitInMs, _telemetryConfig, _fgaConfiguration);
}
}
\ No newline at end of file
diff --git a/src/Fga.Net/FgaConnectionConfiguration.cs b/src/Fga.Net/FgaConnectionConfiguration.cs
index 72e4d6a..52759c3 100644
--- a/src/Fga.Net/FgaConnectionConfiguration.cs
+++ b/src/Fga.Net/FgaConnectionConfiguration.cs
@@ -26,6 +26,7 @@ internal sealed record FgaBuiltConfiguration(
string? AuthorizationModelId,
int? MaxRetry,
int? MinWaitInMs,
+ TelemetryConfig? TelemetryConfig,
FgaConnectionConfiguration Connection);
internal sealed record FgaConnectionConfiguration(string ApiUrl, Credentials? Credentials);
\ No newline at end of file
diff --git a/src/Fga.Net/ServiceCollectionExtensions.cs b/src/Fga.Net/ServiceCollectionExtensions.cs
index 81c9912..3126f07 100644
--- a/src/Fga.Net/ServiceCollectionExtensions.cs
+++ b/src/Fga.Net/ServiceCollectionExtensions.cs
@@ -89,6 +89,8 @@ private static void ConfigureFgaOptions(this FgaClientConfiguration x, FgaBuiltC
if (config.MinWaitInMs.HasValue)
x.MinWaitInMs = config.MinWaitInMs.Value;
+ x.Telemetry = config.TelemetryConfig;
+
x.Credentials = config.Connection.Credentials;
}
diff --git a/tests/Fga.Net.Tests/Fga.Net.Tests.csproj b/tests/Fga.Net.Tests/Fga.Net.Tests.csproj
index 33319d8..3cc8775 100644
--- a/tests/Fga.Net.Tests/Fga.Net.Tests.csproj
+++ b/tests/Fga.Net.Tests/Fga.Net.Tests.csproj
@@ -8,12 +8,12 @@
-
+
-
-
-
-
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all