{"id":18715147,"url":"https://github.com/cerbos/demo-rest","last_synced_at":"2025-09-18T23:02:38.948Z","repository":{"id":39587196,"uuid":"368893516","full_name":"cerbos/demo-rest","owner":"cerbos","description":"Demo of using Cerbos to secure a Go REST API. ","archived":false,"fork":false,"pushed_at":"2025-03-13T06:59:43.000Z","size":238,"stargazers_count":17,"open_issues_count":0,"forks_count":9,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-04-12T13:08:14.328Z","etag":null,"topics":["access-control","cerbos","go","policy","rest-api","security"],"latest_commit_sha":null,"homepage":"https://cerbos.dev","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cerbos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-19T14:15:44.000Z","updated_at":"2025-03-13T06:59:41.000Z","dependencies_parsed_at":"2024-04-19T14:39:33.229Z","dependency_job_id":"7b40b2c1-501d-4320-acd0-1a3599d7839d","html_url":"https://github.com/cerbos/demo-rest","commit_stats":{"total_commits":27,"total_committers":5,"mean_commits":5.4,"dds":0.2592592592592593,"last_synced_commit":"f57032feddfc8d5350a24d663f45547d8e2247be"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerbos%2Fdemo-rest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerbos%2Fdemo-rest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerbos%2Fdemo-rest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerbos%2Fdemo-rest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cerbos","download_url":"https://codeload.github.com/cerbos/demo-rest/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248571892,"owners_count":21126522,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access-control","cerbos","go","policy","rest-api","security"],"created_at":"2024-11-07T13:07:42.866Z","updated_at":"2025-09-18T23:02:38.917Z","avatar_url":"https://github.com/cerbos.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"Securing a REST API with Cerbos\n===============================\n\nThis project demonstrates how to secure a REST API using Cerbos policies. It also shows how to run Cerbos as a sidecar.\n\n\nHow it works\n------------\n\nHTTP middleware checks the username and password sent with each request against the user database and builds a Cerbos principal object containing roles and attributes.\n\n```go\nprincipal := cerbos.NewPrincipal(username).\n    WithRoles(record.Roles...).\n    WithAttr(\"aisles\", record.Aisles).\n    WithAttr(\"ipAddress\", r.RemoteAddr)\n```\n\nChecking access is as simple as making a call to Cerbos PDP.\n\n```go\nresource := cerbos.NewResource(\"inventory\", item.ID).WithAttr(\"aisle\", item.Aisle)\nallowed, err := cerbos.IsAllowed(ctx, principal, resource, \"DELETE\")\n```\n\n\nThe Store API\n-------------\n\nThe example application is a simple REST API exposed by a fictional e-commmerce service. Only authenticated users can access the API.\n\n| Endpoint | Description | Rules |\n| -------- | ----------- | ------------ |\n| `PUT /store/order`            | Create a new order | Only customers can create orders. Each order must contain at least two items. |\n| `GET /store/order/{orderID}`  | View the order | Customers can only view their own orders. Store employees can view any order. |\n| `POST /store/order/{orderID}` | Update the order | Customers can update their own orders as long as the status is `PENDING` |\n| `DELETE /store/order/{orderID}` | Cancel the order | Customers can cancel their own orders as long the status is `PENDING` |\n| `POST /backoffice/order/{orderID}/status/{status}` | Update order status | Pickers can change status from `PENDING` to `PICKING` and `PICKING` to `PICKED`. Dispatchers can change status from `PICKED` to `DISPATCHED`. Managers can change the status to anything. |\n| `PUT /backoffice/inventory` | Add new item to inventory | Only buyers who are in charge of that category or managers can add new items |\n| `GET /backoffice/inventory/{itemID}` | View item | Any employee can view inventory items |\n| `POST /backoffice/inventory/{itemID}` | Update item | Buyers who are in charge of that category can update the item provided that the new price is within 10% of the previous price. Managers can update without any restrictions |\n| `DELETE /backoffice/inventory/{itemID}` | Remove item | Only buyers who are in charge of that category or managers can remove items |\n| `POST /backoffice/inventory/{itemID}/replenish/{quantity}` | Replenish stock | Only stockers and managers can replenish stock |\n| `POST /backoffice/inventory/{itemID}/pick/{quantity}` | Pick stock | Only pickers and managers can pick stock |\n\n\nThe Cerbos policies for the service are in the `cerbos/policies` directory.\n\n- `store_roles.yaml`: A derived roles definition which defines `order-owner` derived role to identify when someone is accessing their own order.\n- `order_resource.yaml`: A resource policy for the `order` resource encapsulating the rules listed in the table above.\n- `inventory_resource.yaml`: A resource policy for the `inventory` resource encapsulating the rules listed in the table above.\n\n\nAvailable users are:\n\n| Username | Password | Roles |\n| -------- | -------- | ----- |\n| adam     | adamsStrongPassword    | customer |\n| bella    | bellasStrongPassword   | customer, employee, manager |\n| charlie  | charliesStrongPassword | customer, employee, picker |\n| diana    | dianasStrongPassword   | customer, employee, dispatcher |\n| eve      | evesStrongPassword     | customer\n| florence | florencesStrongPassword| customer, employee, buyer (bakery) |\n| george   | georgesStrongPassword  | customer, employee, buyer (dairy) |\n| harry    | harrysStrongPassword   | customer, employee, stocker |\n| jenny    | jennysStrongPassword   | customer, employee, stocker |\n\n\nUse `docker-compose` to start the demo. Here Cerbos is configured to run as a sidecar to the application and communicate over a Unix domain socket.\n\n```sh\ndocker-compose up\n```\n\nAlternatively, if you have a Go toolchain installed and have the Cerbos binary installed using one of the methods listed on https://docs.cerbos.dev/cerbos/latest/installation/binary, you can use `cerbos run` to launch the server as follows:\n\n```sh\n# Launch Cerbos and the test server. Assumes that the cerbos binary is in your $PATH.\ncerbos run --set=storage.disk.directory=cerbos/policies -- go run main.go\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eExamples\u003c/b\u003e\u003c/summary\u003e\n\n\n**Adam tries to create an order with a single item**\n\n```sh\ncurl -i -u adam:adamsStrongPassword -XPUT http://localhost:9999/store/order -d '{\"items\": {\"eggs\": 12}}'\n```\n```\n{\n  \"message\": \"Operation not allowed\"\n}\n```\n\n**Adam has enough items in the order**\n\n```sh\ncurl -i -u adam:adamsStrongPassword -XPUT http://localhost:9999/store/order -d '{\"items\": {\"eggs\": 12, \"milk\": 1}}'\n```\n```\n{\n  \"orderID\": 1\n}\n```\n\n**Adam can view his own order**\n\n```sh\ncurl -i -u adam:adamsStrongPassword -XGET http://localhost:9999/store/order/1\n```\n```\n{\n  \"id\": 1,\n  \"items\": {\n    \"eggs\": 12,\n    \"milk\": 1\n  },\n  \"owner\": \"adam\",\n  \"status\": \"PENDING\"\n}\n```\n\n**Eve cannot view Adam's order**\n\n```sh\ncurl -i -u eve:evesStrongPassword -XGET http://localhost:9999/store/order/1\n```\n```\n{\n  \"message\": \"Operation not allowed\"\n}\n```\n\n**Bella can view Adam's order**\n\n```sh\ncurl -i -u bella:bellasStrongPassword -XGET http://localhost:9999/store/order/1\n```\n```\n{\n  \"id\": 1,\n  \"items\": {\n    \"eggs\": 12,\n    \"milk\": 1\n  },\n  \"owner\": \"adam\",\n  \"status\": \"PENDING\"\n}\n```\n\n**Adam can update his pending order**\n\n```sh\ncurl -i -u adam:adamsStrongPassword -XPOST http://localhost:9999/store/order/1 -d '{\"items\": {\"eggs\": 24, \"milk\": 1, \"bread\": 1}}'\n```\n```\n{\n  \"message\": \"Order updated\"\n}\n```\n\n**Charlie cannot set order status to PICKED because it is not in PICKING status**\n\n```sh\ncurl -i -u charlie:charliesStrongPassword -XPOST http://localhost:9999/backoffice/order/1/status/PICKED\n```\n```\n{\n  \"message\": \"Operation not allowed\"\n}\n```\n\n**Charlie can set order status to PICKING**\n\n```sh\ncurl -i -u charlie:charliesStrongPassword -XPOST http://localhost:9999/backoffice/order/1/status/PICKING\n```\n```\n{\n  \"message\": \"Order status updated\"\n}\n```\n\n**Adam cannot update his order because it is not pending**\n\n```sh\ncurl -i -u adam:adamsStrongPassword -XPOST http://localhost:9999/store/order/1 -d '{\"items\": {\"eggs\": 24, \"milk\": 1, \"bread\": 1}}'\n```\n```\n{\n  \"message\": \"Operation not allowed\"\n}\n```\n\n**Florence can add an item to the bakery aisle**\n\n```sh\ncurl -i -u florence:florencesStrongPassword -XPUT http://localhost:9999/backoffice/inventory -d '{\"id\":\"white_bread\", \"aisle\":\"bakery\", \"price\":110}'\n```\n```\n{\n  \"message\": \"Item added\"\n}\n```\n\n**Florence cannot add an item to the dairy aisle**\n\n```sh\ncurl -i -u florence:florencesStrongPassword -XPUT http://localhost:9999/backoffice/inventory -d '{\"id\":\"skimmed_milk\", \"aisle\":\"dairy\", \"price\":120}'\n```\n```\n{\n  \"message\": \"Operation not allowed\"\n}\n```\n\n**Florence can increase the price of an item up to 10%**\n\n```sh\ncurl -i -u florence:florencesStrongPassword -XPOST http://localhost:9999/backoffice/inventory/white_bread -d '{\"id\":\"white_bread\", \"aisle\":\"bakery\", \"price\":120}'\n```\n```\n{\n  \"message\": \"Item updated\"\n}\n```\n\n**Florence cannot increase the price of an item more than 10%**\n\n```sh\ncurl -i -u florence:florencesStrongPassword -XPOST http://localhost:9999/backoffice/inventory/white_bread -d '{\"id\":\"white_bread\", \"aisle\":\"bakery\", \"price\":220}'\n```\n```\n{\n  \"message\": \"Operation not allowed\"\n}\n```\n\n**Bella can increase the price of an item by any amount**\n\n```sh\ncurl -i -u bella:bellasStrongPassword -XPOST http://localhost:9999/backoffice/inventory/white_bread -d '{\"id\":\"white_bread\", \"aisle\":\"bakery\", \"price\":220}'\n```\n```\n{\n  \"message\": \"Item updated\"\n}\n```\n\n**Harry can replenish stock**\n\n```sh\ncurl -i -u harry:harrysStrongPassword -XPOST http://localhost:9999/backoffice/inventory/white_bread/replenish/10\n```\n```\n{\n  \"newQuantity\": 10\n}\n```\n\n**Harry cannot pick stock**\n\n```sh\ncurl -i -u harry:harrysStrongPassword -XPOST http://localhost:9999/backoffice/inventory/white_bread/pick/1\n```\n```\n{\n  \"message\": \"Operation not allowed\"\n}\n```\n\n**Charlie can pick stock**\n\n```sh\ncurl -i -u charlie:charliesStrongPassword -XPOST http://localhost:9999/backoffice/inventory/white_bread/pick/1\n```\n```\n{\n  \"newQuantity\": 9\n}\n```\n\n**Charlie cannot replenish stock**\n\n```sh\ncurl -i -u charlie:charliesStrongPassword -XPOST http://localhost:9999/backoffice/inventory/white_bread/replenish/10\n```\n```\n{\n  \"message\": \"Operation not allowed\"\n}\n```\n\n**Bella can delete an item from inventory**\n\n```sh\ncurl -i -u bella:bellasStrongPassword -XDELETE http://localhost:9999/backoffice/inventory/white_bread\n```\n```\n{\n  \"message\": \"Item deleted\"\n}\n```\n\u003c/details\u003e\n\n\nGet help\n--------\n\n- Visit the [Cerbos website](https://cerbos.dev)\n- [Join the Cerbos community on Slack](http://go.cerbos.io/slack)\n- Email us at help@cerbos.dev\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcerbos%2Fdemo-rest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcerbos%2Fdemo-rest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcerbos%2Fdemo-rest/lists"}