Skip to content
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

enhancement: Metadata (headers) support #115

Merged
merged 2 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions src/Sdk.UnitTests/Cerbos/Sdk/UnitTests/CerbosClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class CerbosClientTest
private const string Tag = "dev";
private const string PathToPolicies = "./../../../res/policies";
private const string PathToConfig = "./../../../res/config";
private readonly Grpc.Core.Metadata _metadata = new() { { "wibble", "wobble" } };

private IContainer? _container;

private CerbosClient? _client;
Expand All @@ -45,8 +47,8 @@ public void OneTimeSetUp()

Task.Run(async () => await _container.StartAsync()).Wait();
Thread.Sleep(3000);
_client = CerbosClientBuilder.ForTarget("http://127.0.0.1:3593").WithPlaintext().Build();
_clientPlayground = CerbosClientBuilder.ForTarget(PlaygroundHost).WithPlaygroundInstance(PlaygroundInstanceId).Build();
_client = CerbosClientBuilder.ForTarget("http://127.0.0.1:3593").WithMetadata(_metadata).WithPlaintext().Build();
_clientPlayground = CerbosClientBuilder.ForTarget(PlaygroundHost).WithMetadata(_metadata).WithPlaygroundInstance(PlaygroundInstanceId).Build();
}

[OneTimeTearDown]
Expand Down Expand Up @@ -81,7 +83,7 @@ public void CheckWithoutJwt()
)
.WithIncludeMeta(true);

var have = _client.CheckResources(request).Find("XX125");
var have = _client.CheckResources(request, _metadata).Find("XX125");
Assert.That(have.IsAllowed("view:public"), Is.True);
Assert.That(have.IsAllowed("approve"), Is.False);

Expand Down Expand Up @@ -126,7 +128,7 @@ public void CheckWithJwt()
.WithActions("defer")
);

var have = _client.CheckResources(request).Find("XX125");
var have = _client.CheckResources(request, _metadata).Find("XX125");
Assert.That(have.IsAllowed("defer"), Is.True);
}

Expand Down Expand Up @@ -175,7 +177,7 @@ public void CheckMultiple()
);


var have = _client.CheckResources(request);
var have = _client.CheckResources(request, _metadata);
var resourcexx125 = have.Find("XX125");
Assert.That(resourcexx125.IsAllowed("view:public"), Is.True);
Assert.That(resourcexx125.IsAllowed("defer"), Is.True);
Expand Down Expand Up @@ -213,7 +215,7 @@ public void PlanResources()
.WithAction("approve");


var have = _client.PlanResources(request);
var have = _client.PlanResources(request, _metadata);
Assert.That(have.Action, Is.EqualTo("approve"));
Assert.That(have.PolicyVersion, Is.EqualTo("20210210"));
Assert.That(have.ResourceKind, Is.EqualTo("leave_request"));
Expand Down Expand Up @@ -260,7 +262,7 @@ public void PlanResourcesValidation()
)
.WithAction("approve");

var have = _client.PlanResources(request);
var have = _client.PlanResources(request, _metadata);
Assert.That(have.Action, Is.EqualTo("approve"));
Assert.That(have.PolicyVersion, Is.EqualTo("20210210"));
Assert.That(have.ResourceKind, Is.EqualTo("leave_request"));
Expand Down Expand Up @@ -302,7 +304,7 @@ public void Playground()
.WithActions("approve", "delete")
);

var have = _clientPlayground.CheckResources(request).Find("XX125");
var have = _clientPlayground.CheckResources(request, _metadata).Find("XX125");
Assert.That(have.IsAllowed("approve"), Is.True);
Assert.That(have.IsAllowed("delete"), Is.True);
}
Expand Down Expand Up @@ -333,7 +335,7 @@ public async Task CheckWithoutJwtAsync()
.WithActions("approve", "view:public")
);

var have = (await _client.CheckResourcesAsync(request)).Find("XX125");
var have = (await _client.CheckResourcesAsync(request, _metadata)).Find("XX125");
Assert.That(have.IsAllowed("view:public"), Is.True);
Assert.That(have.IsAllowed("approve"), Is.False);

Expand Down
62 changes: 26 additions & 36 deletions src/Sdk/Cerbos/Sdk/Builder/CerbosClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

using System;
using System.IO;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Grpc.Net.Client;

namespace Cerbos.Sdk.Builder
Expand All @@ -20,6 +20,7 @@ public sealed class CerbosClientBuilder
private StreamReader TlsCertificate { get; set; }
private StreamReader TlsKey { get; set; }
private GrpcChannelOptions GrpcChannelOptions { get; set; }
private Metadata Metadata { get; set; }

private CerbosClientBuilder(string target) {
Target = target;
Expand All @@ -30,6 +31,12 @@ public static CerbosClientBuilder ForTarget(string target)
return new CerbosClientBuilder(target);
}

public CerbosClientBuilder WithMetadata(Metadata headers)
{
Metadata = headers;
return this;
}

public CerbosClientBuilder WithPlaintext() {
Plaintext = true;
return this;
Expand Down Expand Up @@ -81,14 +88,15 @@ public CerbosClient Build()
);
}

CallCredentials callCredentials = null;
Metadata combined = Metadata;
if (!string.IsNullOrEmpty(PlaygroundInstanceId))
{
callCredentials = CallCredentials.FromInterceptor((context, metadata) =>
{
metadata.Add(PlaygroundInstanceHeader, PlaygroundInstanceId.Trim());
return Task.CompletedTask;
});
{
combined = Utility.Metadata.Merge(
Metadata,
new Metadata {
{ PlaygroundInstanceHeader, PlaygroundInstanceId.Trim() }
}
);
}

SslCredentials sslCredentials = null;
Expand All @@ -103,39 +111,21 @@ public CerbosClient Build()
sslCredentials = new SslCredentials(CaCertificate.ReadToEnd());
}
}

GrpcChannel channel;
if (Plaintext)
var grpcChannelOptions = GrpcChannelOptions ?? new GrpcChannelOptions();
if (sslCredentials != null)
{
if (GrpcChannelOptions != null)
{
channel = GrpcChannel.ForAddress(Target, GrpcChannelOptions);
}
else
{
channel = GrpcChannel.ForAddress(Target);
}
grpcChannelOptions.Credentials = sslCredentials;
}
else
else if (!Plaintext)
{
GrpcChannelOptions grpcChannelOptions = GrpcChannelOptions ?? new GrpcChannelOptions();
if (callCredentials != null && sslCredentials != null)
{
grpcChannelOptions.Credentials = ChannelCredentials.Create(sslCredentials, callCredentials);
}
else if (sslCredentials != null)
{
grpcChannelOptions.Credentials = sslCredentials;
}
else if (callCredentials != null)
{
grpcChannelOptions.Credentials = ChannelCredentials.Create(ChannelCredentials.SecureSsl, callCredentials);
}

channel = GrpcChannel.ForAddress(Target, grpcChannelOptions);
grpcChannelOptions.Credentials = ChannelCredentials.SecureSsl;
}

return new CerbosClient(new Api.V1.Svc.CerbosService.CerbosServiceClient(channel));
var grpcChannel = GrpcChannel
.ForAddress(Target, grpcChannelOptions)
.Intercept();
return new CerbosClient(new Api.V1.Svc.CerbosService.CerbosServiceClient(grpcChannel), combined);
}
}
}
23 changes: 13 additions & 10 deletions src/Sdk/Cerbos/Sdk/CerbosClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Threading.Tasks;
using Cerbos.Sdk.Response;
using Grpc.Core;

namespace Cerbos.Sdk
{
Expand All @@ -13,20 +14,22 @@ namespace Cerbos.Sdk
public sealed class CerbosClient
{
private Api.V1.Svc.CerbosService.CerbosServiceClient CerbosServiceClient { get; }

public CerbosClient(Api.V1.Svc.CerbosService.CerbosServiceClient cerbosServiceClient)
private readonly Metadata _metadata;

public CerbosClient(Api.V1.Svc.CerbosService.CerbosServiceClient cerbosServiceClient, Metadata metadata = null)
{
CerbosServiceClient = cerbosServiceClient;
_metadata = metadata;
}

/// <summary>
/// Send a request consisting of a principal, resource(s) & action(s) to see if the principal is authorized to do the action(s) on the resource(s).
/// </summary>
public CheckResourcesResponse CheckResources(Builder.CheckResourcesRequest request)
public CheckResourcesResponse CheckResources(Builder.CheckResourcesRequest request, Metadata headers = null)
{
try
{
return new CheckResourcesResponse(CerbosServiceClient.CheckResources(request.ToCheckResourcesRequest()));
return new CheckResourcesResponse(CerbosServiceClient.CheckResources(request.ToCheckResourcesRequest(), Utility.Metadata.Merge(_metadata, headers)));
}
catch (Exception e)
{
Expand All @@ -37,12 +40,12 @@ public CheckResourcesResponse CheckResources(Builder.CheckResourcesRequest reque
/// <summary>
/// Send an async request consisting of a principal, resource(s) & action(s) to see if the principal is authorized to do the action(s) on the resource(s).
/// </summary>
public Task<CheckResourcesResponse> CheckResourcesAsync(Builder.CheckResourcesRequest request)
public Task<CheckResourcesResponse> CheckResourcesAsync(Builder.CheckResourcesRequest request, Metadata headers = null)
{
try
{
return CerbosServiceClient
.CheckResourcesAsync(request.ToCheckResourcesRequest())
.CheckResourcesAsync(request.ToCheckResourcesRequest(), Utility.Metadata.Merge(_metadata, headers))
.ResponseAsync
.ContinueWith(
r => new CheckResourcesResponse(r.Result)
Expand All @@ -57,11 +60,11 @@ public Task<CheckResourcesResponse> CheckResourcesAsync(Builder.CheckResourcesRe
/// <summary>
/// Obtain a query plan for performing the given action on the given resource kind.
/// </summary>
public PlanResourcesResponse PlanResources(Builder.PlanResourcesRequest request)
public PlanResourcesResponse PlanResources(Builder.PlanResourcesRequest request, Metadata headers = null)
{
try
{
return new PlanResourcesResponse(CerbosServiceClient.PlanResources(request.ToPlanResourcesRequest()));
return new PlanResourcesResponse(CerbosServiceClient.PlanResources(request.ToPlanResourcesRequest(), Utility.Metadata.Merge(_metadata, headers)));
}
catch (Exception e)
{
Expand All @@ -72,12 +75,12 @@ public PlanResourcesResponse PlanResources(Builder.PlanResourcesRequest request)
/// <summary>
/// Obtain a query plan for performing the given action on the given resource kind.
/// </summary>
public Task<PlanResourcesResponse> PlanResourcesAsync(Builder.PlanResourcesRequest request)
public Task<PlanResourcesResponse> PlanResourcesAsync(Builder.PlanResourcesRequest request, Metadata headers = null)
{
try
{
return CerbosServiceClient
.PlanResourcesAsync(request.ToPlanResourcesRequest())
.PlanResourcesAsync(request.ToPlanResourcesRequest(), Utility.Metadata.Merge(_metadata, headers))
.ResponseAsync
.ContinueWith(
r => new PlanResourcesResponse(r.Result)
Expand Down
36 changes: 36 additions & 0 deletions src/Sdk/Cerbos/Sdk/Utility/Metadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Cerbos.Sdk.Utility
{
public static class Metadata
{
public static Grpc.Core.Metadata Merge(Grpc.Core.Metadata first, Grpc.Core.Metadata second)
{
if (first == null && second == null)
{
return null;
}

if (first != null && second == null)
{
return first;
}

if (first == null)
{
return second;
}

Grpc.Core.Metadata combined = new Grpc.Core.Metadata();
foreach (var m in first)
{
combined.Add(m);
}

foreach (var m in second)
{
combined.Add(m);
}

return combined;
}
}
}
Loading