Skip to content

Commit

Permalink
enhancement: Metadata (headers) support (#69)
Browse files Browse the repository at this point in the history
### Summary

- Added gRPC Metadata support
- Simplified the internal client-building logic
- Moved the playground-related logic to the cerbos client builder like
other SDKs

### Example
When metadata (`{"wibble": "wobble"}`) is provided during both client
initialization and CheckResources call, the cerbos audit logs show;
```
{
  "log.logger":"cerbos.audit",
  "log.kind":"access",
  "timestamp":"2023-12-13T17:30:40.905891Z",
  "callId":"01HHJ3P3C9FBSGC94Y12NMANTR",
  "peer":{
    "address":"127.0.0.1:51027",
    "userAgent":"grpc-php/1.55.0 grpc-c/32.0.0 (osx; chttp2)"
  },
  "metadata":{
    "wibble":{
      "values":[
        "wobble",
        "wobble"
      ]
    }
  },
  "method":"/cerbos.svc.v1.CerbosService/CheckResources"
}
```

Signed-off-by: Oğuzhan Durgun <[email protected]>
  • Loading branch information
oguzhand95 authored Dec 14, 2023
1 parent 1c86c21 commit 9b7d366
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 30 deletions.
26 changes: 25 additions & 1 deletion src/Sdk/Builder/CerbosClientBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
namespace Cerbos\Sdk\Builder;

use Cerbos\Sdk\CerbosClient;
use Cerbos\Sdk\Utility\Metadata;
use Cerbos\Svc\V1\CerbosServiceClient;
use Exception;
use Grpc\ChannelCredentials;

const playgroundInstanceHeader = "playground-instance";

class CerbosClientBuilder
{
private string $hostname;
Expand All @@ -20,6 +23,7 @@ class CerbosClientBuilder
private ?string $caCertificate;
private ?string $tlsCertificate;
private ?string $tlsKey;
private ?array $metadata;

/**
* @param string $hostname
Expand All @@ -31,6 +35,7 @@ private function __construct(string $hostname) {
$this->caCertificate = null;
$this->tlsCertificate = null;
$this->tlsKey = null;
$this->metadata = null;
}

/**
Expand All @@ -41,6 +46,15 @@ public static function newInstance(string $hostname): CerbosClientBuilder {
return new CerbosClientBuilder($hostname);
}

/**
* @param array<string, array> $headers
* @return CerbosClientBuilder
*/
public function withMetadata(array $headers): CerbosClientBuilder {
$this->metadata = $headers;
return $this;
}

/**
* @param bool $plaintext
* @return CerbosClientBuilder
Expand Down Expand Up @@ -126,6 +140,16 @@ public function build(): CerbosClient {
]
);

return new CerbosClient($csc, $this->playgroundInstanceId);
$combined = $this->metadata;
if ($this->playgroundInstanceId != "") {
$combined = Metadata::merge(
$this->metadata,
[
playgroundInstanceHeader => [ $this->playgroundInstanceId ]
]
);
}

return new CerbosClient($csc, $combined);
}
}
30 changes: 11 additions & 19 deletions src/Sdk/CerbosClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,58 +11,50 @@
use Cerbos\Sdk\Builder\PlanResourcesRequest;
use Cerbos\Sdk\Response\V1\CheckResourcesResponse\CheckResourcesResponse;
use Cerbos\Sdk\Response\V1\PlanResourcesResponse\PlanResourcesResponse;
use Cerbos\Sdk\Utility\Metadata;
use Cerbos\Svc\V1\CerbosServiceClient;
use Exception;

class CerbosClient
{
private CerbosServiceClient $csc;

private string $playgroundInstanceId;
private string $playgroundInstanceHeader = "playground-instance";
private ?array $metadata;

/**
* @param CerbosServiceClient $csc
* @param string $playgroundInstanceId
* @param array<string, array> $headers
*/
public function __construct(CerbosServiceClient $csc, string $playgroundInstanceId) {
public function __construct(CerbosServiceClient $csc, $headers = null) {
$this->csc = $csc;
$this->playgroundInstanceId = $playgroundInstanceId;
$this->metadata = $headers;
}

/**
* @param CheckResourcesRequest $request
* @param array<string, array> $headers
* @return CheckResourcesResponse
* @throws Exception
*/
public function checkResources(CheckResourcesRequest $request): CheckResourcesResponse {
list($checkResourcesResponse, $status) = $this->csc->CheckResources($request->toCheckResourcesRequest(), $this->getMetadata())->wait();
public function checkResources(CheckResourcesRequest $request, $headers = null): CheckResourcesResponse {
list($checkResourcesResponse, $status) = $this->csc->CheckResources($request->toCheckResourcesRequest(), Metadata::merge($this->metadata, $headers))->wait();
$this->handleError($status);

return new CheckResourcesResponse($checkResourcesResponse);
}

/**
* @param PlanResourcesRequest $request
* @param array<string, array> $headers
* @return PlanResourcesResponse
* @throws Exception
*/
public function planResources(PlanResourcesRequest $request): PlanResourcesResponse {
list($planResourcesResponse, $status) = $this->csc->PlanResources($request->toPlanResourcesRequest(), $this->getMetadata())->wait();
public function planResources(PlanResourcesRequest $request, $headers = null): PlanResourcesResponse {
list($planResourcesResponse, $status) = $this->csc->PlanResources($request->toPlanResourcesRequest(), Metadata::merge($this->metadata, $headers))->wait();
$this->handleError($status);

return new PlanResourcesResponse($planResourcesResponse);
}

/**
* @return array<string, array>
*/
private function getMetadata(): array {
return array(
$this->playgroundInstanceHeader => [$this->playgroundInstanceId]
);
}

/**
* @param object $status
* @throws Exception
Expand Down
53 changes: 53 additions & 0 deletions src/Sdk/Utility/Metadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

// Copyright 2021-2023 Zenauth Ltd.
// SPDX-License-Identifier: Apache-2.0

declare(strict_types=1);

namespace Cerbos\Sdk\Utility;

use Exception;

class Metadata
{
/**
* @param array|null $first
* @param array|null $second
* @return array<string, array>
* @throws Exception
*/
public static function merge(?array $first, ?array $second): array {
if (is_null($first) && is_null($second))
return [];
else if (is_null($first) && !is_null($second))
return $second;
else if (is_null($second))
return $first;

$combined = array();
foreach ($first as $k => $v) {
if (is_array($v)) {
foreach ($v as $i => $vv) {
$combined[$k][] = $vv;
}
}
else {
throw new Exception("the type for the parameter first and second parameter is array<string, array>|null");
}
}

foreach ($second as $k => $v) {
if (is_array($v)) {
foreach ($v as $i => $vv) {
$combined[$k][] = $vv;
}
}
else {
throw new Exception("the type for the parameter first and second parameter is array<string, array>|null");
}
}

return $combined;
}
}
14 changes: 7 additions & 7 deletions tests/CerbosClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function testCheckWithoutJwt(): void
);

try {
$checkResourcesResult = $this->client->checkResources($request);
$checkResourcesResult = $this->client->checkResources($request, $this->metadata);
$resultEntry = $checkResourcesResult->find("XX125");
} catch (Exception $e) {
$this->fail($e->getMessage());
Expand Down Expand Up @@ -83,7 +83,7 @@ public function testCheckWithJwt(): void {
);

try {
$checkResourcesResult = $this->client->checkResources($request);
$checkResourcesResult = $this->client->checkResources($request, $this->metadata);
$resultEntry = $checkResourcesResult->find("XX125");
} catch (Exception $e) {
$this->fail($e->getMessage());
Expand Down Expand Up @@ -137,7 +137,7 @@ public function testCheckMultiple(): void {
);

try {
$checkResourcesResult = $this->client->checkResources($request);
$checkResourcesResult = $this->client->checkResources($request, $this->metadata);
$resultEntryXX125 = $checkResourcesResult->find("XX125");
$resultEntryXX225 = $checkResourcesResult->find("XX225");
$resultEntryXX325 = $checkResourcesResult->find("XX325");
Expand Down Expand Up @@ -177,7 +177,7 @@ public function testValidationErrors(): void
);

try {
$checkResourcesResult = $this->client->checkResources($request);
$checkResourcesResult = $this->client->checkResources($request, $this->metadata);
$resultEntry = $checkResourcesResult->find("XX125");
} catch (Exception $e) {
$this->fail($e->getMessage());
Expand Down Expand Up @@ -208,7 +208,7 @@ public function testPlanResources(): void{


try {
$planResourcesResult = $this->client->planResources($request);
$planResourcesResult = $this->client->planResources($request, $this->metadata);
} catch (Exception $e) {
$this->fail($e->getMessage());
}
Expand Down Expand Up @@ -256,7 +256,7 @@ public function testPlanResourcesValidation(): void{
->withAction("approve");

try {
$planResourcesResult = $this->client->planResources($request);
$planResourcesResult = $this->client->planResources($request, $this->metadata);
} catch (Exception $e) {
$this->fail($e->getMessage());
}
Expand Down Expand Up @@ -294,7 +294,7 @@ public function testPlayground(): void {
);

try {
$checkResourcesResult = $this->playgroundClient->checkResources($request);
$checkResourcesResult = $this->playgroundClient->checkResources($request, $this->metadata);
$resultEntry = $checkResourcesResult->find("XX125");
} catch (Exception $e) {
$this->fail($e->getMessage());
Expand Down
12 changes: 9 additions & 3 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
private string $host = 'localhost:3593';
private string $playgroundHost = 'demo-pdp.cerbos.cloud';
private string $playgroundInstanceId = 'XhkOi82fFKk3YW60e2c806Yvm0trKEje'; // See: https://play.cerbos.dev/p/XhkOi82fFKk3YW60e2c806Yvm0trKEje
protected array $metadata = ["wibble" => ["wobble"]];
protected CerbosClient $client;
protected CerbosClient $playgroundClient;

Expand All @@ -26,8 +27,13 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
protected function setUp(): void
{
parent::setUp();

$this->client = CerbosClientBuilder::newInstance($this->host)->withPlaintext(true)->build();
$this->playgroundClient = CerbosClientBuilder::newInstance($this->playgroundHost)->withPlayground($this->playgroundInstanceId)->build();
$this->client = CerbosClientBuilder::newInstance($this->host)
->withMetadata($this->metadata)
->withPlaintext(true)
->build();
$this->playgroundClient = CerbosClientBuilder::newInstance($this->playgroundHost)
->withMetadata($this->metadata)
->withPlayground($this->playgroundInstanceId)
->build();
}
}

0 comments on commit 9b7d366

Please sign in to comment.