# 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.43.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.43.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	tressuilder	rue	rue;\n use Cerbos
tent	y	rues
ti	rue;;
use Cerbos<|vq_2369|>        include below step to generate cerbos.json file while updating the fyre learning while updating the results to be indexed.
    
    include below step to generate cerbos.json file while updating the fyre learning while updating the results to be indexed.

$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.43.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

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

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

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

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

Response

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.43.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)
