https://github.com/open-policy-agent/opa-csharp
A driver to connect via C# to Open Policy Agent (OPA) deployments
https://github.com/open-policy-agent/opa-csharp
opa open-policy-agent sdk-csharp sdk-dotnet sdk-net
Last synced: 8 months ago
JSON representation
A driver to connect via C# to Open Policy Agent (OPA) deployments
- Host: GitHub
- URL: https://github.com/open-policy-agent/opa-csharp
- Owner: open-policy-agent
- License: apache-2.0
- Created: 2024-03-06T18:58:52.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-10-05T01:11:10.000Z (8 months ago)
- Last Synced: 2025-10-06T21:58:52.222Z (8 months ago)
- Topics: opa, open-policy-agent, sdk-csharp, sdk-dotnet, sdk-net
- Language: C#
- Homepage: https://open-policy-agent.github.io/opa-csharp/
- Size: 509 KB
- Stars: 6
- Watchers: 7
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
- awesome-opa - C# SDK - C# SDK for interacting with OPA ([documentation](https://open-policy-agent.github.io/opa-csharp/)) (Language and Platform Integrations / .NET)
README
# OPA C# SDK
[](https://opensource.org/licenses/Apache-2.0)
[](https://www.nuget.org/packages/OpenPolicyAgent.Opa/)
> [!IMPORTANT]
> Reference documentation is available at
You can use the OPA C# SDK to connect to [Open Policy Agent](https://www.openpolicyagent.org/) and [EOPA](https://github.com/open-policy-agent/eopa) deployments.
## SDK Installation
### Nuget
```bash
dotnet add package OpenPolicyAgent.Opa
```
## SDK Example Usage (high-level)
The following examples assume an OPA server at `http://localhost:8181` equipped with the following Rego policy in `authz.rego`:
```rego
package authz
import rego.v1
default allow := false
allow if input.subject == "alice"
```
and this `data.json`:
```json
{
"roles": {
"admin": ["read", "write"]
}
}
```
### Simple Query
For a simple boolean response with input, use the SDK as follows:
```csharp
using OpenPolicyAgent.Opa;
string opaUrl = "http://localhost:8181";
OpaClient opa = new OpaClient(opaUrl);
var input = new Dictionary() {
{"subject", "alice"},
{"action", "read"},
};
bool allowed = false;
try
{
allowed = await opa.Check("authz/allow", input);
}
catch (OpaException e)
{
Console.WriteLine("exception while making request against OPA: " + e);
}
Console.WriteLine("allowed: " + allowed);
```
Result
```txt
allowed: True
```
### Simple Query with Output
The `.Evaluate()` method can be used instead of `.Check()` for non-boolean output types:
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using OpenPolicyAgent.Opa;
var opaUrl = "http://localhost:8181";
var opa = new OpaClient(opaUrl);
var input = new Dictionary() {
{"subject", "alice"},
{"action", "read"},
};
var result = new Dictionary>();
try
{
result = await opa.Evaluate>>("roles", input);
}
catch (OpaException e)
{
Console.WriteLine("exception while making request against OPA: " + e.Message);
}
Console.WriteLine("content of data.roles:");
foreach (var pair in result)
{
Console.Write(" {0} => [ ", pair.Key);
foreach (var item in pair.Value)
{
Console.Write("{0} ", item);
}
Console.WriteLine("]");
}
```
Result
```txt
content of data.roles:
admin => [ read write ]
```
### Default Rule
For evaluating the default rule (configured with your OPA service), use `EvaluateDefault`. `input` is optional, and left out in this example:
```csharp
using OpenPolicyAgent.Opa;
string opaUrl = "http://localhost:8181";
OpaClient opa = new OpaClient(opaUrl);
bool allowed = false;
try {
allowed = await opa.EvaluateDefault
Result
```txt
allowed: False
```
### Batched Queries
EOPA supports executing many queries in a single request with the [Batch API][eopa-batch-api].
[eopa-batch-api]: https://github.com/open-policy-agent/eopa/blob/main/docs/eopa/reference/api-reference/batch-api.md
The OPA C# SDK has native support for EOPA's batch API, with a fallback behavior of sequentially executing single queries if the Batch API is unavailable (such as with open source Open Policy Agent).
```csharp
using OpenPolicyAgent.Opa;
string opaUrl = "http://localhost:8181";
OpaClient opa = new OpaClient(opaUrl);
var input = new Dictionary>() {
{ "AAA", new Dictionary() { { "subject", "alice" }, { "action", "read" } } },
{ "BBB", new Dictionary() { { "subject", "bob" }, { "action", "write" } } },
{ "CCC", new Dictionary() { { "subject", "dave" }, { "action", "read" } } },
{ "DDD", new Dictionary() { { "subject", "sybil" }, { "action", "write" } } },
};
OpaBatchResults results = new OpaBatchResults();
OpaBatchErrors errors = new OpaBatchErrors();
try
{
(results, errors) = await opa.EvaluateBatch("authz/allow", input);
}
catch (OpaException e)
{
Console.WriteLine("exception while making request against OPA: " + e.Message);
}
Console.WriteLine("Query results, by key:");
foreach (var pair in results)
{
Console.WriteLine(" {0} => {1}", pair.Key, pair.Value.Result.Boolean);
}
if (errors.Count > 0)
{
Console.WriteLine("Query errors, by key:");
foreach (var pair in errors)
{
Console.WriteLine(" {0} => {1}", pair.Key, pair.Value);
}
}
```
Result
```txt
Query results, by key:
AAA => True
BBB => False
CCC => False
DDD => False
```
See the [API Documentation](https://open-policy-agent.github.io/opa-csharp/api/OpenPolicyAgent.Opa.OpenApi.Models.Components.Result.html) for reference on the properties and types available from a result.
### Using Custom Classes for Input and Output
Using the OPA C# SDK, it can be more natural to use custom class types as inputs and outputs to a policy, rather than `System.Collections.Dictionary` (or `Collections.List`). Internally, the OPA C# SDK uses [`Newtonsoft.Json`](https://www.newtonsoft.com/json) to serialize and deserialize inputs and outputs JSON to the provided types.
In the example below, note:
- Using an `enum` for an input field
- Hiding the sensitive `UUID` with the `JsonIgnore` property
- Deserializing the query response to a `bool`
```csharp
using System;
using OpenPolicyAgent.Opa;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Application
{
class Program
{
public enum ActionType
{
invalid,
create,
read,
update,
delete
}
private class CustomRBACObject
{
[JsonProperty("user")]
public string User = "";
[JsonProperty("action")]
[JsonConverter(typeof(StringEnumConverter))]
public ActionType Action = ActionType.invalid;
[JsonIgnore]
public string UUID = System.Guid.NewGuid().ToString();
public CustomRBACObject() { }
public CustomRBACObject(string user, ActionType action)
{
User = user;
Action = action;
}
}
static async Task Main(string[] args)
{
string opaUrl = "http://localhost:8181";
OpaClient opa = new OpaClient(opaUrl);
var input = new CustomRBACObject("bob", ActionType.read);
Console.WriteLine("The JSON that OPA will receive: {{\"input\": {0}}}", JsonConvert.SerializeObject(input));
bool allowed = false;
try
{
allowed = await opa.Evaluate("authz/allow", input);
}
catch (OpaException e)
{
Console.WriteLine("exception while making request against OPA: " + e.Message);
}
Console.WriteLine("allowed: " + allowed);
return 0;
}
}
}
```
Result
```txt
The JSON that OPA will receive: {"input": {"user":"bob","action":"read"}}
allowed: False
```
### Integrating logging with the OPA C# SDK
The OPA C# SDK uses opt-in, [compile-time source generated logging](https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator), which can be integrated as a part of the overall logs of a larger application.
Here's a quick example:
```csharp
using Microsoft.Extensions.Logging;
using OpenPolicyAgent.Opa;
internal class Program
{
static async Task Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger();
var opaURL = "http://localhost:8181";
OpaClient opa = new OpaClient(opaURL, logger);
logger.LogInformation("Initialized an OPA client for the OPA at: {Description}.", opaURL);
var allow = await opa.Evaluate("this/rule/does/not/exist", false);
return 0;
}
}
```
Result
```log
info: OpenPolicyAgent.Opa.OpaClient[0]
Initialized an OPA client for the OPA at: http://localhost:8181.
warn: OpenPolicyAgent.Opa.OpaClient[2066302899]
executing policy at 'this/rule/does/not/exist' succeeded, but OPA did not reply with a result
Unhandled exception. OpaException: executing policy at 'this/rule/does/not/exist' succeeded, but OPA did not reply with a result
...
```
> [!NOTE]
> For low-level SDK usage, see the sections below.
---
# OPA OpenAPI SDK (low-level)
## Summary
For more information about the API: [EOPA documentation](https://github.com/open-policy-agent/eopa/docs)
## Table of Contents
- [OPA C# SDK](#opa-c-sdk)
- [SDK Installation](#sdk-installation)
- [SDK Example Usage (high-level)](#sdk-example-usage-high-level)
- [OPA OpenAPI SDK (low-level)](#opa-openapi-sdk-low-level)
- [SDK Example Usage](#sdk-example-usage)
- [Available Resources and Operations](#available-resources-and-operations)
- [Server Selection](#server-selection)
- [Error Handling](#error-handling)
- [Authentication](#authentication)
- [Community](#community)
## SDK Example Usage
### Example 1
```csharp
using OpenPolicyAgent.Opa.OpenApi;
using OpenPolicyAgent.Opa.OpenApi.Models.Components;
var sdk = new OpaApiClient();
var res = await sdk.ExecuteDefaultPolicyWithInputAsync(
input: Input.CreateNumber(
4963.69D
),
pretty: false,
acceptEncoding: GzipAcceptEncoding.Gzip
);
// handle response
```
### Example 2
```csharp
using OpenPolicyAgent.Opa.OpenApi;
using OpenPolicyAgent.Opa.OpenApi.Models.Requests;
var sdk = new OpaApiClient();
ExecutePolicyWithInputRequest req = new ExecutePolicyWithInputRequest() {
Path = "app/rbac",
RequestBody = new ExecutePolicyWithInputRequestBody() {
Input = Input.CreateBoolean(
false
),
},
};
var res = await sdk.ExecutePolicyWithInputAsync(req);
// handle response
```
### Example 3
```csharp
using OpenPolicyAgent.Opa.OpenApi;
using OpenPolicyAgent.Opa.OpenApi.Models.Components;
using OpenPolicyAgent.Opa.OpenApi.Models.Requests;
using System.Collections.Generic;
var sdk = new OpaApiClient();
ExecuteBatchPolicyWithInputRequest req = new ExecuteBatchPolicyWithInputRequest() {
Path = "app/rbac",
RequestBody = new ExecuteBatchPolicyWithInputRequestBody() {
Inputs = new Dictionary() {
{ "key", Input.CreateStr(
""
) },
},
},
};
var res = await sdk.ExecuteBatchPolicyWithInputAsync(req);
// handle response
```
## Available Resources and Operations
Available methods
### [OpaApiClient SDK](docs/sdks/opaapiclient/README.md)
- [ExecuteDefaultPolicyWithInput](docs/sdks/opaapiclient/README.md#executedefaultpolicywithinput) - Execute the default decision given an input
- [ExecutePolicy](docs/sdks/opaapiclient/README.md#executepolicy) - Execute a policy
- [ExecutePolicyWithInput](docs/sdks/opaapiclient/README.md#executepolicywithinput) - Execute a policy given an input
- [ExecuteBatchPolicyWithInput](docs/sdks/opaapiclient/README.md#executebatchpolicywithinput) - Execute a policy given a batch of inputs
- [CompileQueryWithPartialEvaluation](docs/sdks/opaapiclient/README.md#compilequerywithpartialevaluation) - Partially evaluate a query
- [Health](docs/sdks/opaapiclient/README.md#health) - Verify the server is operational
## Server Selection
### Override Server URL Per-Client
The default server can be overridden globally by passing a URL to the `serverUrl: string` optional parameter when initializing the SDK client instance. For example:
```csharp
using OpenPolicyAgent.Opa.OpenApi;
using OpenPolicyAgent.Opa.OpenApi.Models.Components;
var sdk = new OpaApiClient(serverUrl: "http://localhost:8181");
var res = await sdk.ExecuteDefaultPolicyWithInputAsync(
input: Input.CreateNumber(
4963.69D
),
pretty: false,
acceptEncoding: GzipAcceptEncoding.Gzip
);
// handle response
```
## Error Handling
Handling errors in this SDK should largely match your expectations. All operations return a response object or throw an exception.
By default, an API error will raise a `OpenPolicyAgent.Opa.OpenApi.Models.Errors.SDKException` exception, which has the following properties:
| Property | Type | Description |
|---------------|-----------------------|-----------------------|
| `Message` | *string* | The error message |
| `StatusCode` | *int* | The HTTP status code |
| `RawResponse` | *HttpResponseMessage* | The raw HTTP response |
| `Body` | *string* | The response content |
When custom error responses are specified for an operation, the SDK may also throw their associated exceptions. You can refer to respective *Errors* tables in SDK docs for more details on possible exception types for each operation. For example, the `ExecuteDefaultPolicyWithInputAsync` method throws the following exceptions:
| Error Type | Status Code | Content Type |
| -------------------------------------------- | ----------- | ---------------- |
| OpenPolicyAgent.Opa.OpenApi.Models.Errors.ClientError | 400, 404 | application/json |
| OpenPolicyAgent.Opa.OpenApi.Models.Errors.ServerError | 500 | application/json |
| OpenPolicyAgent.Opa.OpenApi.Models.Errors.SDKException | 4XX, 5XX | \*/\* |
### Example
```csharp
using OpenPolicyAgent.Opa.OpenApi;
using OpenPolicyAgent.Opa.OpenApi.Models.Components;
using OpenPolicyAgent.Opa.OpenApi.Models.Errors;
var sdk = new OpaApiClient();
try
{
var res = await sdk.ExecuteDefaultPolicyWithInputAsync(
input: Input.CreateNumber(
4963.69D
),
pretty: false,
acceptEncoding: GzipAcceptEncoding.Gzip
);
// handle response
}
catch (Exception ex)
{
if (ex is ClientError)
{
// Handle exception data
throw;
}
else if (ex is Models.Errors.ServerError)
{
// Handle exception data
throw;
}
else if (ex is OpenPolicyAgent.Opa.OpenApi.Models.Errors.SDKException)
{
// Handle default exception
throw;
}
}
```
## Authentication
### Per-Client Security Schemes
This SDK supports the following security scheme globally:
| Name | Type | Scheme |
| ------------ | ---- | ----------- |
| `BearerAuth` | http | HTTP Bearer |
To authenticate with the API the `BearerAuth` parameter must be set when initializing the SDK client instance. For example:
```csharp
using OpenPolicyAgent.Opa.OpenApi;
using OpenPolicyAgent.Opa.OpenApi.Models.Components;
var sdk = new OpaApiClient(bearerAuth: "");
var res = await sdk.ExecuteDefaultPolicyWithInputAsync(
input: Input.CreateNumber(
4963.69D
),
pretty: false,
acceptEncoding: GzipAcceptEncoding.Gzip
);
// handle response
```
## Community
For questions, discussions and announcements, please join
the OPA community on [Slack](https://slack.openpolicyagent.org/)!