Opt-out Preferences

We use third-party cookies that help us analyze how you use this website, store your preferences, and provide the content and advertisements that are relevant to you. However, you can opt out of these cookies by checking "Do Not Sell or Share My Personal Information" and clicking the "Save My Preferences" button. Once you opt out, you can opt in again at any time by unchecking "Do Not Sell or Share My Personal Information" and clicking the "Save My Preferences" button.

Do Not Sell or Share My Personal Information

Quickstart

This documentation is for a previous version of Cerbos. Choose 0.40.0 from the version picker at the top right or navigate to https://docs.cerbos.dev for the latest version.

Create a directory to store the policies.

mkdir -p cerbos-quickstart/policies
sh

Now start the Cerbos server. We are using the container image in this guide but you can follow along using the binary as well. See installation instructions for more information.

docker run --rm --name cerbos -d -v $(pwd)/cerbos-quickstart/policies:/policies -p 3592:3592 -p 3593:3593  ghcr.io/cerbos/cerbos:0.32.0
shell

Time to try out a simple request.

If you prefer to use Postman, Insomnia or any other software that supports OpenAPI, you can follow this guide along on those tools by downloading the OpenAPI definitions from http://localhost:3592/schema/swagger.json. You can also use the built-in API browser by pointing your browser to http://localhost:3592.
  • cURL

  • .NET

  • Go

  • Java

  • JS

  • PHP

  • Python

  • Ruby

  • Rust

cat <<EOF | curl --silent "http://localhost:3592/api/check/resources?pretty" -d @-
{
  "requestId": "quickstart",
  "principal": {
    "id": "bugs_bunny",
    "roles": [
      "user"
    ],
    "attr": {
      "beta_tester": true
    }
  },
  "resources": [
    {
      "actions": [
        "view:public",
        "comment"
      ],
      "resource": {
        "kind": "album:object",
        "id": "BUGS001",
        "attr": {
          "owner": "bugs_bunny",
          "public": false,
          "flagged": false
        }
      }
    },
    {
      "actions": [
        "view:public",
        "comment"
      ],
      "resource": {
        "kind": "album:object",
        "id": "DAFFY002",
        "attr": {
          "owner": "daffy_duck",
          "public": true,
          "flagged": false
        }
      }
    }
  ]
}
EOF
shell
using Cerbos.Api.V1.Effect;
using Cerbos.Sdk.Response;
using Cerbos.Sdk.Builder;
using Cerbos.Sdk.Utility;

internal class Program
{
    private static void Main(string[] args)
    {
        var client = CerbosClientBuilder.ForTarget("http://localhost:3593").WithPlaintext().Build();
        var request = CheckResourcesRequest
            .NewInstance()
            .WithRequestId(RequestId.Generate())
            .WithIncludeMeta(true)
            .WithPrincipal(
                Principal
                    .NewInstance("bugs_bunny", "user")
                    .WithAttribute("beta_tester", AttributeValue.BoolValue(true))
            )
            .WithResourceEntries(
                ResourceEntry
                    .NewInstance("album:object", "BUGS001")
                    .WithAttribute("owner", AttributeValue.StringValue("bugs_bunny"))
                    .WithAttribute("public", AttributeValue.BoolValue(false))
                    .WithAttribute("flagged", AttributeValue.BoolValue(false))
                    .WithActions("comment", "view:public"),

                ResourceEntry
                    .NewInstance("album:object", "DAFFY002")
                    .WithPolicyVersion("20210210")
                    .WithAttribute("owner", AttributeValue.StringValue("daffy_duck"))
                    .WithAttribute("public", AttributeValue.BoolValue(true))
                    .WithAttribute("flagged", AttributeValue.BoolValue(false))
                    .WithActions("comment", "view:public")
            );

        CheckResourcesResponse result = client.CheckResources(request);
        foreach (var resourceId in new[] { "BUGS001", "DAFFY002" })
        {
            var resultEntry = result.Find(resourceId);
            Console.Write($"\nResource ID: {resourceId}\n");
            foreach (var actionEffect in resultEntry.Actions)
            {
                string action = actionEffect.Key;
                Effect effect = actionEffect.Value;
                Console.Write($"\t{action} -> {(effect == Effect.Allow ? "EFFECT_ALLOW" : "EFFECT_DENY")}\n");
            }
        }
    }
}
csharp
package main

import (
	"context"
	"log"

	"github.com/cerbos/cerbos-sdk-go/cerbos"
)

func main() {
	c, err := cerbos.New("localhost:3593", cerbos.WithPlaintext())
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	principal := cerbos.NewPrincipal("bugs_bunny", "user")
	principal.WithAttr("beta_tester", true)

	kind := "album:object"
	actions := []string{"view:public", "comment"}

	r1 := cerbos.NewResource(kind, "BUGS001")
	r1.WithAttributes(map[string]any{
		"owner":   "bugs_bunny",
		"public":  false,
		"flagged": false,
	})

	r2 := cerbos.NewResource(kind, "DAFFY002")
	r2.WithAttributes(map[string]any{
		"owner":   "daffy_duck",
		"public":  true,
		"flagged": false,
	})

	batch := cerbos.NewResourceBatch()
	batch.Add(r1, actions...)
	batch.Add(r2, actions...)

	resp, err := c.CheckResources(context.Background(), principal, batch)
	if err != nil {
		log.Fatalf("Failed to check resources: %v", err)
	}
	log.Printf("%v", resp)
}
go
package demo;

import static dev.cerbos.sdk.builders.AttributeValue.boolValue;
import static dev.cerbos.sdk.builders.AttributeValue.stringValue;

import java.util.Map;

import dev.cerbos.sdk.CerbosBlockingClient;
import dev.cerbos.sdk.CerbosClientBuilder;
import dev.cerbos.sdk.CheckResult;
import dev.cerbos.sdk.builders.Principal;
import dev.cerbos.sdk.builders.ResourceAction;


public class App {
    public static void main(String[] args) throws CerbosClientBuilder.InvalidClientConfigurationException {
        CerbosBlockingClient client=new CerbosClientBuilder("localhost:3593").withPlaintext().buildBlockingClient();

        for (String n : new String[]{"BUGS001", "DAFFY002"}) {
            CheckResult cr = client.batch(
                Principal.newInstance("bugs_bunny", "user")
                    .withAttribute("beta_tester", boolValue(true))
                )
                .addResources(
                    ResourceAction.newInstance("album:object","BUGS001")
                        .withAttributes(
                            Map.of(
                                "owner", stringValue("bugs_bunny"),
                                "public", boolValue(false),
                                "flagged", boolValue(false)
                            )
                        )
                        .withActions("view:public", "comment"),
                    ResourceAction.newInstance("album:object","DAFFY002")
                        .withAttributes(
                            Map.of(
                                "owner", stringValue("daffy_duck"),
                                "public", boolValue(true),
                                "flagged", boolValue(false)
                            )
                        )
                        .withActions("view:public", "comment")
                )
                .check().find(n).orElse(null);

            if (cr != null) {
                System.out.printf("\nResource: %s\n", n);
                cr.getAll().forEach((action, allowed) -> { System.out.printf("\t%s -> %s\n", action, allowed ? "EFFECT_ALLOW" : "EFFECT_DENY"); });
            }
        }
    }
}
java
const { GRPC: Cerbos } = require("@cerbos/grpc");

const cerbos = new Cerbos("localhost:3593", { tls: false });

(async() => {
  const kind = "album:object";
  const actions = ["view:public", "comment"];

  const cerbosPayload = {
    principal: {
      id: "bugs_bunny",
      roles: ["user"],
      attributes: {
        beta_tester: true,
      },
    },
    resources: [
      {
        resource: {
          kind: kind,
          id: "BUGS001",
          attributes: {
		    owner:   "bugs_bunny",
		    public:  false,
		    flagged: false,
          },
        },
        actions: actions,
      },
      {
        resource: {
          kind: kind,
          id: "DAFFY002",
          attributes: {
		    owner:   "daffy_duck",
		    public:  true,
		    flagged: false,
          },
        },
        actions: actions,
      },
    ],
  };

  const decision = await cerbos.checkResources(cerbosPayload);
  console.log(decision.results)
})();
javascript
<?php

require __DIR__ . '/vendor/autoload.php';

use Cerbos\Effect\V1\Effect;
use Cerbos\Sdk\Builder\AttributeValue;
use Cerbos\Sdk\Builder\CerbosClientBuilder;
use Cerbos\Sdk\Builder\CheckResourcesRequest;
use Cerbos\Sdk\Builder\Principal;
use Cerbos\Sdk\Builder\ResourceEntry;
use Cerbos\Sdk\Utility\RequestId;

$client = CerbosClientBuilder::newInstance("localhost:3593")
            ->withPlaintext(true)
            ->build();

$request = CheckResourcesRequest::newInstance()
    ->withRequestId(RequestId::generate())
    ->withPrincipal(
        Principal::newInstance("bugs_bunny")
            ->withRole("user")
            ->withAttribute("beta_tester", AttributeValue::boolValue(true))
    )
    ->withResourceEntries(
        [
            ResourceEntry::newInstance("album:object", "BUGS001")
                ->withAttribute("owner", AttributeValue::stringValue("bugs_bunny"))
                ->withAttribute("public", AttributeValue::boolValue(false))
                ->withAttribute("flagged", AttributeValue::boolValue(false))
                ->withActions(["comment", "view:public"]),

            ResourceEntry::newInstance("album:object", "DAFFY002")
                ->withAttribute("owner", AttributeValue::stringValue("daffy_duck"))
                ->withAttribute("public", AttributeValue::boolValue(true))
                ->withAttribute("flagged", AttributeValue::boolValue(false))
                ->withActions(["comment", "view:public"])
        ]
    );

$checkResourcesResponse = $client->checkResources($request);
foreach (["BUGS001", "DAFFY002"] as $resourceId) {
    $resultEntry = $checkResourcesResponse->find($resourceId);
    $actions = $resultEntry->getActions();
    foreach ($actions as $k => $v) {
        printf("%s -> %s", $k, Effect::name($v));
    }
}
?>
php
import json

from cerbos.sdk.client import CerbosClient
from cerbos.sdk.model import Principal, Resource, ResourceAction, ResourceList
from fastapi import HTTPException, status

principal = Principal(
    "bugs_bunny",
    roles=["user"],
    attr={
        "beta_tester": True,
    },
)

actions = ["view:public", "comment"]
resource_list = ResourceList(
    resources=[
        ResourceAction(
            Resource(
                "BUGS001",
                "album:object",
                attr={
                    "owner": "bugs_bunny",
                    "public": False,
                    "flagged": False,
                },
            ),
            actions=actions,
        ),
        ResourceAction(
            Resource(
                "DAFFY002",
                "album:object",
                attr={
                    "owner": "daffy_duck",
                    "public": True,
                    "flagged": False,
                },
            ),
            actions=actions,
        ),
    ],
)

with CerbosClient(host="http://localhost:3592") as c:
    try:
        resp = c.check_resources(principal=principal, resources=resource_list)
        resp.raise_if_failed()
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="Unauthorized"
        )

print(json.dumps(resp.to_dict(), sort_keys=False, indent=4))
python
require 'cerbos'
require 'json'

client = Cerbos::Client.new("localhost:3593", tls: false)

kind = "album:object"
actions = ["view:public", "comment"]

r1 = {
  :kind => kind,
  :id => "BUGS001",
  :attributes => {
    :owner => "bugs_bunny",
    :public => false,
    :flagged => false,
  }
}

r2 = {
  :kind => kind,
  :id => "DAFFY002",
  :attributes => {
    :owner => "daffy_duck",
    :public => true,
    :flagged => false,
  }
}

decision = client.check_resources(
  principal: {
    id: "bugs_bunny",
    roles: ["user"],
    attributes: {
      beta_tester: true,
    },
  },
  resources: [
    {
      resource: r1,
      actions: actions
    },
    {
      resource: r2,
      actions: actions
    },
  ],
)

res = {
  :results => [
    {
      :resource => r1,
      :actions => {
        :comment => decision.allow?(resource: r1, action: "comment"),
        :"view:public" => decision.allow?(resource: r1, action: "view:public"),
      },
    },
    {
      :resource => r2,
      :actions => {
        :comment => decision.allow?(resource: r2, action: "comment"),
        :"view:public" => decision.allow?(resource: r2, action: "view:public"),
      },
    },
  ],
}
puts JSON.pretty_generate(res)
ruby
use cerbos::sdk::attr::attr;
use cerbos::sdk::model::{Principal, Resource, ResourceAction, ResourceList};
use cerbos::sdk::{CerbosAsyncClient, CerbosClientOptions, CerbosEndpoint, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let opt =
        CerbosClientOptions::new(CerbosEndpoint::HostPort("localhost", 3593)).with_plaintext();
    let mut client = CerbosAsyncClient::new(opt).await?;

    let principal =
        Principal::new("bugs_bunny", ["user"]).with_attributes([attr("beta_tester", true)]);

    let actions: [&str; 2] = ["view:public", "comment"];

    let resp = client
        .check_resources(
            principal,
            ResourceList::new_from([
                ResourceAction(
                    Resource::new("BUGS001", "album:object").with_attributes([
                        attr("owner", "bugs_bunny"),
                        attr("public", false),
                        attr("flagged", false),
                    ]),
                    actions,
                ),
                ResourceAction(
                    Resource::new("DAFFY002", "album:object").with_attributes([
                        attr("owner", "daffy_duck"),
                        attr("public", true),
                        attr("flagged", false),
                    ]),
                    actions,
                ),
            ]),
            None,
        )
        .await?;

    println!("{:?}", resp.response);

    Ok(())
}
rust

In this example, the bugs_bunny principal is trying to perform two actions (view:public and comment) on two album:object resources. The resource instance with the ID BUGS001 belongs to bugs_bunny and is private (public attribute is false). The other resource instance with the ID DAFFY002 belongs to daffy_duck and is public.

This is the response from the server:

Response
{
  "requestId": "quickstart",
  "results": [
    {
      "resource": {
        "id": "BUGS001",
        "kind": "album:object"
      },
      "actions": {
        "comment": "EFFECT_DENY",
        "view:public": "EFFECT_DENY"
      }
    },
    {
      "resource": {
        "id": "DAFFY002",
        "kind": "album:object"
      },
      "actions": {
        "comment": "EFFECT_DENY",
        "view:public": "EFFECT_DENY"
      }
    }
  ]
}
json

Bugs Bunny is not allowed to view or comment on any of the album resources — even the ones that belong to him. This is because currently there are no policies defined for the album:object resource.

Now create a derived roles definition that assigns the owner dynamic role to a user if the owner attribute of the resource they’re trying to access is equal to their ID.

cat > cerbos-quickstart/policies/derived_roles_common.yaml <<EOF
---
apiVersion: "api.cerbos.dev/v1"
derivedRoles:
  name: common_roles
  definitions:
    - name: owner
      parentRoles: ["user"]
      condition:
        match:
          expr: request.resource.attr.owner == request.principal.id
EOF
sh

Also create a resource policy that gives owners full access to their own albums.

cat > cerbos-quickstart/policies/resource_album.yaml <<EOF
---
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: "default"
  importDerivedRoles:
    - common_roles
  resource: "album:object"
  rules:
    - actions: ['*']
      effect: EFFECT_ALLOW
      derivedRoles:
        - owner
EOF
sh

Try the request again. This time bugs_bunny should be allowed access to his own album but denied access to the album owned by daffy_duck.

Request
cat <<EOF | curl --silent "http://localhost:3592/api/check/resources?pretty" -d @-
{
  "requestId": "quickstart",
  "principal": {
    "id": "bugs_bunny",
    "roles": [
      "user"
    ],
    "attr": {
      "beta_tester": true
    }
  },
  "resources": [
    {
      "actions": [
        "view:public",
        "comment"
      ],
      "resource": {
        "kind": "album:object",
        "id": "BUGS001",
        "attr": {
          "owner": "bugs_bunny",
          "public": false,
          "flagged": false
        }
      }
    },
    {
      "actions": [
        "view:public",
        "comment"
      ],
      "resource": {
        "kind": "album:object",
        "id": "DAFFY002",
        "attr": {
          "owner": "daffy_duck",
          "public": true,
          "flagged": false
        }
      }
    }
  ]
}
EOF
shell
Response
{
  "requestId": "quickstart",
  "results": [
    {
      "resource": {
        "id": "BUGS001",
        "kind": "album:object"
      },
      "actions": {
        "comment": "EFFECT_ALLOW",
        "view:public": "EFFECT_ALLOW"
      }
    },
    {
      "resource": {
        "id": "DAFFY002",
        "kind": "album:object"
      },
      "actions": {
        "comment": "EFFECT_DENY",
        "view:public": "EFFECT_DENY"
      }
    }
  ]
}
json

Now add a rule to the policy to allow users to view public albums.

cat > cerbos-quickstart/policies/resource_album.yaml <<EOF
---
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: "default"
  importDerivedRoles:
    - common_roles
  resource: "album:object"
  rules:
    - actions: ['*']
      effect: EFFECT_ALLOW
      derivedRoles:
        - owner

    - actions: ['view:public']
      effect: EFFECT_ALLOW
      roles:
        - user
      condition:
        match:
          expr: request.resource.attr.public == true
EOF
sh

If you try the request again, bugs_bunny now has view:public access to the album owned by daffy_duck but not comment access. Can you figure out how to update the policy to give him comment access as well?

Request and response
Request
cat <<EOF | curl --silent "http://localhost:3592/api/check/resources?pretty" -d @-
{
  "requestId": "quickstart",
  "principal": {
    "id": "bugs_bunny",
    "roles": [
      "user"
    ],
    "attr": {
      "beta_tester": true
    }
  },
  "resources": [
    {
      "actions": [
        "view:public",
        "comment"
      ],
      "resource": {
        "kind": "album:object",
        "id": "BUGS001",
        "attr": {
          "owner": "bugs_bunny",
          "public": false,
          "flagged": false
        }
      }
    },
    {
      "actions": [
        "view:public",
        "comment"
      ],
      "resource": {
        "kind": "album:object",
        "id": "DAFFY002",
        "attr": {
          "owner": "daffy_duck",
          "public": true,
          "flagged": false
        }
      }
    }
  ]
}
EOF
shell
Response
{
  "requestId": "quickstart",
  "results": [
    {
      "resource": {
        "id": "BUGS001",
        "kind": "album:object"
      },
      "actions": {
        "comment": "EFFECT_ALLOW",
        "view:public": "EFFECT_ALLOW"
      }
    },
    {
      "resource": {
        "id": "DAFFY002",
        "kind": "album:object"
      },
      "actions": {
        "comment": "EFFECT_DENY",
        "view:public": "EFFECT_ALLOW"
      }
    }
  ]
}
json

Once you are done experimenting, the Cerbos server can be stopped with the following command:

docker kill cerbos
shell