Template
1
0

feat: update cerbos to use json

This commit is contained in:
2025-11-23 13:44:39 +00:00
parent 7504361d88
commit 2b462993cc
12 changed files with 120 additions and 188 deletions

View File

@@ -8,7 +8,6 @@ server:
grpcListenAddr: ":3593" grpcListenAddr: ":3593"
storage: storage:
driver: disk driver: "sqlite3"
disk: sqlite3:
directory: /data/policies dsn: "file:/tmp/cerbos.sqlite?mode=rwc&cache=shared&_fk=true"
watchForChanges: true

View File

@@ -6,32 +6,18 @@
"apps/react", "apps/react",
"modules/iam", "modules/iam",
"modules/workspace", "modules/workspace",
"platform/cerbos",
"platform/config", "platform/config",
"platform/database", "platform/database",
"platform/logger", "platform/logger",
"platform/relay", "platform/relay",
"platform/routes",
"platform/server", "platform/server",
"platform/socket", "platform/socket",
"platform/spec", "platform/spec",
"platform/storage", "platform/storage",
"platform/vault" "platform/vault"
], ],
"imports": {
"@modules/iam/client.ts": "./modules/iam/client.ts",
"@modules/iam/server.ts": "./modules/iam/server.ts",
"@modules/iam/types.ts": "./modules/iam/types.ts",
"@modules/workspace/client.ts": "./modules/workspace/client.ts",
"@modules/workspace/server.ts": "./modules/workspace/server.ts",
"@platform/config/": "./platform/config/",
"@platform/database/": "./platform/database/",
"@platform/logger": "./platform/logger/mod.ts",
"@platform/relay": "./platform/relay/mod.ts",
"@platform/server/": "./platform/server/",
"@platform/socket/": "./platform/socket/",
"@platform/spec/": "./platform/spec/",
"@platform/storage": "./platform/storage/storage.ts",
"@platform/vault": "./platform/vault/vault.ts"
},
"tasks": { "tasks": {
"start:api": { "start:api": {
"command": "cd ./api && deno run start", "command": "cd ./api && deno run start",

97
deno.lock generated
View File

@@ -4,7 +4,9 @@
"npm:@biomejs/biome@*": "2.2.4", "npm:@biomejs/biome@*": "2.2.4",
"npm:@biomejs/biome@2.2.4": "2.2.4", "npm:@biomejs/biome@2.2.4": "2.2.4",
"npm:@cerbos/core@0.24.1": "0.24.1", "npm:@cerbos/core@0.24.1": "0.24.1",
"npm:@cerbos/core@0.25.1": "0.25.1_@bufbuild+protobuf@2.10.1",
"npm:@cerbos/http@0.23.1": "0.23.1", "npm:@cerbos/http@0.23.1": "0.23.1",
"npm:@cerbos/http@0.23.3": "0.23.3_@bufbuild+protobuf@2.10.1_@cerbos+api@0.2.0",
"npm:@eslint/js@9.35.0": "9.35.0", "npm:@eslint/js@9.35.0": "9.35.0",
"npm:@jsr/std__assert@1.0.14": "1.0.14", "npm:@jsr/std__assert@1.0.14": "1.0.14",
"npm:@jsr/std__dotenv@0.225.5": "0.225.5", "npm:@jsr/std__dotenv@0.225.5": "0.225.5",
@@ -14,14 +16,14 @@
"npm:@jsr/valkyr__event-store@2.0.1": "2.0.1", "npm:@jsr/valkyr__event-store@2.0.1": "2.0.1",
"npm:@jsr/valkyr__inverse@1.0.1": "1.0.1", "npm:@jsr/valkyr__inverse@1.0.1": "1.0.1",
"npm:@jsr/valkyr__json-rpc@1.1.0": "1.1.0", "npm:@jsr/valkyr__json-rpc@1.1.0": "1.1.0",
"npm:@tailwindcss/vite@4.1.13": "4.1.13_vite@7.1.6__picomatch@4.0.3_@types+node@24.2.0", "npm:@tailwindcss/vite@4.1.13": "4.1.13_vite@7.1.6__@types+node@24.2.0__picomatch@4.0.3_@types+node@24.2.0",
"npm:@tanstack/react-query@5.89.0": "5.89.0_react@19.1.1", "npm:@tanstack/react-query@5.89.0": "5.89.0_react@19.1.1",
"npm:@tanstack/react-router-devtools@1.131.47": "1.131.47_@tanstack+react-router@1.131.47__react@19.1.1__react-dom@19.1.1___react@19.1.1_react@19.1.1_react-dom@19.1.1__react@19.1.1", "npm:@tanstack/react-router-devtools@1.131.47": "1.131.47_@tanstack+react-router@1.131.47__react@19.1.1__react-dom@19.1.1___react@19.1.1_react@19.1.1_react-dom@19.1.1__react@19.1.1",
"npm:@tanstack/react-router@1.131.47": "1.131.47_react@19.1.1_react-dom@19.1.1__react@19.1.1", "npm:@tanstack/react-router@1.131.47": "1.131.47_react@19.1.1_react-dom@19.1.1__react@19.1.1",
"npm:@types/node@*": "24.2.0", "npm:@types/node@*": "24.2.0",
"npm:@types/react-dom@19.1.9": "19.1.9_@types+react@19.1.13", "npm:@types/react-dom@19.1.9": "19.1.9_@types+react@19.1.13",
"npm:@types/react@19.1.13": "19.1.13", "npm:@types/react@19.1.13": "19.1.13",
"npm:@vitejs/plugin-react@4.7.0": "4.7.0_vite@7.1.6__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.2.0", "npm:@vitejs/plugin-react@4.7.0": "4.7.0_vite@7.1.6__@types+node@24.2.0__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.2.0",
"npm:better-auth@1.3.16": "1.3.16_react@19.1.1_react-dom@19.1.1__react@19.1.1", "npm:better-auth@1.3.16": "1.3.16_react@19.1.1_react-dom@19.1.1__react@19.1.1",
"npm:cookie@1.0.2": "1.0.2", "npm:cookie@1.0.2": "1.0.2",
"npm:eslint-plugin-react-hooks@5.2.0": "5.2.0_eslint@9.35.0", "npm:eslint-plugin-react-hooks@5.2.0": "5.2.0_eslint@9.35.0",
@@ -38,7 +40,7 @@
"npm:tailwindcss@4.1.13": "4.1.13", "npm:tailwindcss@4.1.13": "4.1.13",
"npm:typescript-eslint@8.44.0": "8.44.0_eslint@9.35.0_typescript@5.9.2_@typescript-eslint+parser@8.44.0__eslint@9.35.0__typescript@5.9.2", "npm:typescript-eslint@8.44.0": "8.44.0_eslint@9.35.0_typescript@5.9.2_@typescript-eslint+parser@8.44.0__eslint@9.35.0__typescript@5.9.2",
"npm:typescript@5.9.2": "5.9.2", "npm:typescript@5.9.2": "5.9.2",
"npm:vite@7.1.6": "7.1.6_picomatch@4.0.3_@types+node@24.2.0", "npm:vite@7.1.6": "7.1.6_@types+node@24.2.0_picomatch@4.0.3",
"npm:zod@4.1.11": "4.1.11" "npm:zod@4.1.11": "4.1.11"
}, },
"npm": { "npm": {
@@ -239,16 +241,42 @@
"os": ["win32"], "os": ["win32"],
"cpu": ["x64"] "cpu": ["x64"]
}, },
"@bufbuild/protobuf@2.10.1": {
"integrity": "sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg=="
},
"@cerbos/api@0.2.0": {
"integrity": "sha512-p3kAfmgz0WwxXBJ8Dt1vugV/QjQoPtF5b1R+h16YnUPZ6O4YL8D9gjz5WQRg/0FllodyaEtJlrMPxGEvJetkIw==",
"dependencies": [
"@bufbuild/protobuf"
]
},
"@cerbos/core@0.24.1": { "@cerbos/core@0.24.1": {
"integrity": "sha512-Gt9ETQR3WDVcPlxN+HiGUDtNgWFulwS5ZjBgzJFsdb7e2GCw0tOPE9Ex1qHNZvG/0JHpFWJWIiYaSKyXcp35YQ==", "integrity": "sha512-Gt9ETQR3WDVcPlxN+HiGUDtNgWFulwS5ZjBgzJFsdb7e2GCw0tOPE9Ex1qHNZvG/0JHpFWJWIiYaSKyXcp35YQ==",
"dependencies": [ "dependencies": [
"uuid" "uuid"
] ]
}, },
"@cerbos/core@0.25.1_@bufbuild+protobuf@2.10.1": {
"integrity": "sha512-aPi8IqqgGHq9xyoBk6dYAKQ1U1athW0YZfI+7lzxxwpLlNFdZ9EwJLhaRSUFgYpUS2TBWDtX094Yn1kgB1esCQ==",
"dependencies": [
"@bufbuild/protobuf",
"@cerbos/api",
"uuid"
]
},
"@cerbos/http@0.23.1": { "@cerbos/http@0.23.1": {
"integrity": "sha512-XzWFS6L7M+oUnjGEFIoQygtlmZy3zOpUobN6spGp1MAaT6GQJMRFK8P8xhY2BQjTIhqYgnoiEFOAULTkbgNIjg==", "integrity": "sha512-XzWFS6L7M+oUnjGEFIoQygtlmZy3zOpUobN6spGp1MAaT6GQJMRFK8P8xhY2BQjTIhqYgnoiEFOAULTkbgNIjg==",
"dependencies": [ "dependencies": [
"@cerbos/core", "@cerbos/core@0.24.1",
"qs"
]
},
"@cerbos/http@0.23.3_@bufbuild+protobuf@2.10.1_@cerbos+api@0.2.0": {
"integrity": "sha512-yf8s4v+T4sf/ZiLorHpXhdStOr+q5XEjF2m/yvpcR7E/7e5eGCr5yEov9NIgfRQg1HGW8h+B6CIFBl9amSsaGw==",
"dependencies": [
"@bufbuild/protobuf",
"@cerbos/api",
"@cerbos/core@0.25.1_@bufbuild+protobuf@2.10.1",
"qs" "qs"
] ]
}, },
@@ -891,22 +919,13 @@
], ],
"scripts": true "scripts": true
}, },
"@tailwindcss/vite@4.1.13_vite@7.1.6__picomatch@4.0.3": { "@tailwindcss/vite@4.1.13_vite@7.1.6__@types+node@24.2.0__picomatch@4.0.3_@types+node@24.2.0": {
"integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==", "integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==",
"dependencies": [ "dependencies": [
"@tailwindcss/node", "@tailwindcss/node",
"@tailwindcss/oxide", "@tailwindcss/oxide",
"tailwindcss", "tailwindcss",
"vite@7.1.6_picomatch@4.0.3" "vite"
]
},
"@tailwindcss/vite@4.1.13_vite@7.1.6__picomatch@4.0.3_@types+node@24.2.0": {
"integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==",
"dependencies": [
"@tailwindcss/node",
"@tailwindcss/oxide",
"tailwindcss",
"vite@7.1.6_picomatch@4.0.3_@types+node@24.2.0"
] ]
}, },
"@tanstack/history@1.131.2": { "@tanstack/history@1.131.2": {
@@ -1140,7 +1159,7 @@
"eslint-visitor-keys@4.2.1" "eslint-visitor-keys@4.2.1"
] ]
}, },
"@vitejs/plugin-react@4.7.0_vite@7.1.6__picomatch@4.0.3_@babel+core@7.28.4": { "@vitejs/plugin-react@4.7.0_vite@7.1.6__@types+node@24.2.0__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.2.0": {
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
"dependencies": [ "dependencies": [
"@babel/core", "@babel/core",
@@ -1149,19 +1168,7 @@
"@rolldown/pluginutils", "@rolldown/pluginutils",
"@types/babel__core", "@types/babel__core",
"react-refresh", "react-refresh",
"vite@7.1.6_picomatch@4.0.3" "vite"
]
},
"@vitejs/plugin-react@4.7.0_vite@7.1.6__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.2.0": {
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
"dependencies": [
"@babel/core",
"@babel/plugin-transform-react-jsx-self",
"@babel/plugin-transform-react-jsx-source",
"@rolldown/pluginutils",
"@types/babel__core",
"react-refresh",
"vite@7.1.6_picomatch@4.0.3_@types+node@24.2.0"
] ]
}, },
"acorn-jsx@5.3.2_acorn@8.15.0": { "acorn-jsx@5.3.2_acorn@8.15.0": {
@@ -2259,22 +2266,7 @@
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
"bin": true "bin": true
}, },
"vite@7.1.6_picomatch@4.0.3": { "vite@7.1.6_@types+node@24.2.0_picomatch@4.0.3": {
"integrity": "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==",
"dependencies": [
"esbuild",
"fdir",
"picomatch@4.0.3",
"postcss",
"rollup",
"tinyglobby"
],
"optionalDependencies": [
"fsevents"
],
"bin": true
},
"vite@7.1.6_picomatch@4.0.3_@types+node@24.2.0": {
"integrity": "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==", "integrity": "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==",
"dependencies": [ "dependencies": [
"@types/node", "@types/node",
@@ -2390,6 +2382,14 @@
] ]
} }
}, },
"platform/cerbos": {
"packageJson": {
"dependencies": [
"npm:@cerbos/core@0.25.1",
"npm:@cerbos/http@0.23.3"
]
}
},
"platform/config": { "platform/config": {
"packageJson": { "packageJson": {
"dependencies": [ "dependencies": [
@@ -2423,6 +2423,13 @@
] ]
} }
}, },
"platform/routes": {
"packageJson": {
"dependencies": [
"npm:zod@4.1.11"
]
}
},
"platform/server": { "platform/server": {
"packageJson": { "packageJson": {
"dependencies": [ "dependencies": [

View File

@@ -1,3 +1,11 @@
networks:
server:
name: server
volumes:
mongo:
driver: local
services: services:
# MongoDB # MongoDB
@@ -5,37 +13,33 @@ services:
# Used by event store and read store for managing and reading application data. # Used by event store and read store for managing and reading application data.
mongo: mongo:
image: mongo:8
restart: unless-stopped restart: unless-stopped
image: mongo:8
container_name: boilerplate_mongo
ports: ports:
- "27017:27017" - 6017:27017
environment: environment:
MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password MONGO_INITDB_ROOT_PASSWORD: password
volumes: volumes:
- ./.volumes/mongo/local:/data/db - mongo:/data/db
networks: networks:
- localdev - server
# Cerbos # Cerbos
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
# Policy engine for application access control. # Policy engine for application access control.
cerbos: cerbos:
container_name: cerbos restart: unless-stopped
image: ghcr.io/cerbos/cerbos:latest image: ghcr.io/cerbos/cerbos:latest
container_name: boilerplate_cerbos
command: ["server", "--config=/config.yaml"] command: ["server", "--config=/config.yaml"]
ports: ports:
- "3592:3592" - 6592:3592
- "3593:3593" - 6593:3593
- "3594:3594" - 6594:3594
volumes: volumes:
- ./cerbos.yaml:/config.yaml - ./cerbos.yaml:/config.yaml
- ./modules/identity/cerbos/policies:/data/policies/identity
- ./modules/workspace/cerbos/policies:/data/policies/workspace
networks: networks:
- localdev - server
networks:
localdev:
driver: bridge

View File

@@ -1,33 +0,0 @@
# yaml-language-server: $schema=https://api.cerbos.dev/latest/cerbos/policy/v1/Policy.schema.json
# docs: https://docs.cerbos.dev/cerbos/latest/policies/resource_policies
apiVersion: api.cerbos.dev/v1
resourcePolicy:
resource: workspace
version: default
rules:
- actions: ["create"]
effect: EFFECT_ALLOW
roles: ["super"]
- actions: ["read"]
effect: EFFECT_ALLOW
roles: ["super", "admin", "user"]
condition:
match:
expr: R.attr.workspaceId in P.attr.workspaceIds
- actions: ["update"]
effect: EFFECT_ALLOW
roles: ["super", "admin"]
condition:
match:
expr: R.attr.workspaceId in P.attr.workspaceIds
- actions: ["delete"]
effect: EFFECT_ALLOW
roles: ["super"]
condition:
match:
expr: R.attr.workspaceId in P.attr.workspaceIds

View File

@@ -1,54 +0,0 @@
# yaml-language-server: $schema=https://api.cerbos.dev/latest/cerbos/policy/v1/Policy.schema.json
# docs: https://docs.cerbos.dev/cerbos/latest/policies/resource_policies
apiVersion: api.cerbos.dev/v1
resourcePolicy:
resource: workspace_user
version: default
rules:
# Admins can invite new members into their own workspace
- actions:
- invite
effect: EFFECT_ALLOW
roles:
- admin
condition:
match:
expr: request.principal.workspaceIds.includes(request.resource.workspaceId)
# Admins can remove members from their own workspace
- actions:
- remove
effect: EFFECT_ALLOW
roles:
- admin
condition:
match:
expr: request.principal.workspaceIds.includes(request.resource.workspaceId)
# Admins can update member roles in their own workspace
- actions:
- update_role
effect: EFFECT_ALLOW
roles:
- admin
condition:
match:
expr: request.principal.workspaceIds.includes(request.resource.workspaceId)
# Admins and users can list/read members of their own workspace
- actions:
- list
- read
effect: EFFECT_ALLOW
roles:
- admin
- user
condition:
match:
expr: request.principal.workspaceIds.includes(request.resource.workspaceId)

View File

@@ -1,20 +0,0 @@
/*
export const resources = new ResourceRegistry([
{
kind: "workspace",
actions: [],
attr: {
workspaceId: z.string(),
},
},
{
kind: "workspace_user",
actions: [],
attr: {
workspaceId: z.string(),
},
},
] as const);
export type Resource = typeof resources.$resource;
*/

View File

@@ -0,0 +1,10 @@
{
"name": "@platform/cerbos",
"version": "0.0.0",
"private": true,
"type": "module",
"dependencies": {
"@cerbos/core": "0.25.1",
"@cerbos/http": "0.23.3"
}
}

View File

@@ -11,7 +11,7 @@ export const config = {
port: getEnvironmentVariable({ port: getEnvironmentVariable({
key: "DB_MONGO_PORT", key: "DB_MONGO_PORT",
type: z.coerce.number(), type: z.coerce.number(),
fallback: "27017", fallback: "67017",
}), }),
user: getEnvironmentVariable({ user: getEnvironmentVariable({
key: "DB_MONGO_USER", key: "DB_MONGO_USER",

View File

@@ -0,0 +1,10 @@
{
"name": "@platform/routes",
"version": "0.0.0",
"private": true,
"type": "module",
"dependencies": {
"@platform/relay": "workspace:*",
"zod": "4.1.11"
}
}

View File

@@ -0,0 +1,8 @@
import { route } from "@platform/relay";
import z from "zod";
export default route.post("/api/v1/sessions/search").query({
offset: z.number().min(0).default(0),
limit: z.number().min(10).max(100).default(100),
asc: z.boolean().default(true),
});

15
policy.json Normal file
View File

@@ -0,0 +1,15 @@
{
"$schema": "https://api.cerbos.dev/v0.47.0/cerbos/policy/v1/Policy.schema.json",
"apiVersion": "api.cerbos.dev/v1",
"resourcePolicy": {
"resource": "workspace",
"version": "1",
"rules": [
{
"actions": ["create"],
"effect": "EFFECT_ALLOW",
"roles": ["super"]
}
]
}
}