# Quickstart

|     |     |
| --- | --- |
|  | This documentation is for<br>a previous<br>version of Cerbos. Choose 0.53.0 from the version picker at the top right or navigate to [https://docs.cerbos.dev](https://docs.cerbos.dev/) for the latest version. |

Create a directory to store the policies.

```sh
mkdir -p cerbos-quickstart/policies
```

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](https://docs.cerbos.dev/cerbos/0.39.0/installation/binary) for more information.

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

Time to try out a simple request.

|     |     |
| --- | --- |
|  | If you prefer to use [Postman](https://www.postman.com/), [Insomnia](https://insomnia.rest/) 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](http://localhost:3592/schema/swagger.json). You can also use the built-in API browser by pointing your browser to [http://localhost:3592](http://localhost:3592/). |

- cURL
- .NET
- Go
- Java
- JS
- PHP
- Python
- Ruby
- Rust

```shell
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
```

```csharp
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")
                    .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")}");
            }
        }
    }
}
```

```go
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)
}
```

```java
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"); });
            }
        }
    }
}
```

```javascript
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)
})();
```

```php
<?php

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

use Cerbosfectesponse;
use Cerbosuildersuildequestid;

$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));
    }
}
?>
```

```python
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))
```

```ruby
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)
```

```rust
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(())
}
```

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

```json
{
  "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"
      }
    }
  ]
}
```

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](https://docs.cerbos.dev/cerbos/0.39.0/policies/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.

```sh
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
```

Also create a resource policy that gives `owner`s full access to their own albums.

```sh
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
```

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

Response

```json
{
  "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"
      }
    }
  ]
}
```

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

```sh
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
```

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

Response

```json
{
  "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"
      }
    }
  ]
}
```

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

```shell
docker kill cerbos
```

Next steps
- [Explore the demo apps built with Cerbos](https://github.com/cerbos?q=demo&type=all&language=&sort=)
- [Read more about Cerbos policies](https://docs.cerbos.dev/cerbos/0.39.0/policies/)
- [Join the Cerbos community on Slack](http://go.cerbos.io/slack)
- [Ask us anything via](mailto:help@cerbos.dev) [help@cerbos.dev](mailto:help@cerbos.dev)
- [Visit the Cerbos website](/content/site-root.html)
