From fa680b24d1123f9de27fc752943e43c86c692314 Mon Sep 17 00:00:00 2001 From: UMTS at Teleco Date: Sun, 15 Feb 2026 15:53:50 +0100 Subject: JAYSON DERULO --- toolkits/README.md | 11 ++++ toolkits/endpoints.md | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ toolkits/overview.md | 42 +++++++++++++++ toolkits/permissions.md | 68 ++++++++++++++++++++++++ 4 files changed, 258 insertions(+) create mode 100644 toolkits/README.md create mode 100644 toolkits/endpoints.md create mode 100644 toolkits/overview.md create mode 100644 toolkits/permissions.md (limited to 'toolkits') diff --git a/toolkits/README.md b/toolkits/README.md new file mode 100644 index 0000000..814c1f8 --- /dev/null +++ b/toolkits/README.md @@ -0,0 +1,11 @@ +# Toolkits + +[back to index](../README.md) + +The plugin system. Toolkits extend the server with custom tables, endpoints, permissions and scheduled tasks. + +## Pages + +- [Overview](overview.md) what toolkits are and the two types +- [Endpoints](endpoints.md) custom API endpoints (internal, HTTP proxy, executable) +- [Permissions](permissions.md) toolkit groups, associations and how permissions are resolved diff --git a/toolkits/endpoints.md b/toolkits/endpoints.md new file mode 100644 index 0000000..760895e --- /dev/null +++ b/toolkits/endpoints.md @@ -0,0 +1,137 @@ +# Toolkit Endpoints + +[back to toolkits](README.md) /// [home](../README.md) + +Custom API routes defined by toolkits. These live under `/tk/app/{toolkit}/` or `/tk/lib/{toolkit}/` depending on the toolkit type. + +## Route format + +``` +POST /tk/app/beepzone/kiosk/scan +GET /tk/lib/opensigma/status +``` + +The path after the toolkit name is matched against endpoint definitions in the toolkit config. + +## Endpoint types + +### Internal + +Runs logic within the server process. Can use `pseudo_querys` to execute database queries based on the request payload. No external service needed. + +### HTTP + +Proxies the request to an external HTTP service. The server wraps the payload in a context object (user info, DB context etc), sends it to the upstream URL and returns the response. + +Can return JSON (wrapped in the standard envelope) or `passthrough` (raw bytes with the upstream content type, useful for file downloads). + +### Executable + +Runs a shell command on the server. The request context is passed as a JSON string argument. Output is parsed as JSON or wrapped in `{ "output": "..." }` if its not valid JSON. + +## Request flow + +1. verify the toolkit exists and is enabled (404 if not) +2. match the endpoint path in config (404 if not found) +3. check endpoint level permissions for your power level (403 if denied) +4. validate payload against `client_filter` rules (400 if invalid) +5. fetch `inject_db_context` values from the database +6. resolve your toolkit group +7. build the request context +8. apply `overwrite_wrapping` transformations +9. execute based on endpoint type + +## Request context + +The context object sent to backends looks like this: + +```json +{ + "user_id": 1, + "username": "admin", + "power": 100, + "core_group_id": 1, + "core_group_name": "administrators", + "toolkit_group": "managers", + "request_id": "abc123", + "payload": { ... }, + "db_context": { "key": "value" } +} +``` + +`toolkit_group` is the users group within this specific toolkit (resolved via `jde_associations` or `toolkit_overrides`). + +`db_context` contains values fetched via `inject_db_context` config. + +## Client filter + +Validates the incoming request payload before anything else happens. + +```toml +[endpoints.0.client_filter] +allowed_fields = ["barcode", "quantity"] +required_fields = ["barcode"] +max_body_bytes = 4096 +``` + +| Field | Notes | +|-------------------------|------------------------------------------------------------------| +| `allowed_fields` | only these fields are accepted, everything else gets stripped | +| `required_fields` | these must be present or you get 400 | +| `max_body_bytes` | max payload size | +| `allowed_content_types` | restrict accepted content types | +| `field_rules` | per field validation (type, max_length, pattern, allowed_values) | + +Per field rules example: + +```toml +[endpoints.0.client_filter.field_rules.barcode] +type = "string" +max_length = 50 +pattern = "^[A-Z0-9-]+$" +``` + +## Database context injection + +`inject_db_context` lets you pull values from the database and include them in the request context. + +Simple: pull a value by column name +```toml +inject_db_context = ["users.email"] +``` + +Filtered: pull specific rows based on payload values +```toml +inject_db_context = ["items.name?id=$payload.item_id"] +``` + +The `$payload.field` syntax references fields from the clients request body. + +## Overwrite wrapping + +Modify the context envelope before sending it to the backend. + +- prefix with `-` to remove a field: `"-power"` removes the power level from context +- without prefix: promotes a db_context value to a top level field + +## Success response + +```json +{ + "success": true, + "request_id": "abc123", + "endpoint": "/tk/app/beepzone/kiosk/scan", + "data": { ... } +} +``` + +## Error responses + +| Code | When | +|------|-----------------------------------------------------------| +| 400 | payload validation failed | +| 401 | no token or invalid session | +| 403 | endpoint permission denied | +| 404 | toolkit not found/disabled, endpoint path doesnt exist | +| 500 | internal error, DB context fetch failed, executable error | +| 502 | HTTP upstream returned an error | diff --git a/toolkits/overview.md b/toolkits/overview.md new file mode 100644 index 0000000..93b955c --- /dev/null +++ b/toolkits/overview.md @@ -0,0 +1,42 @@ +# Toolkit Overview + +[back to toolkits](README.md) /// [home](../README.md) + +A toolkit is a bundle of tables, permissions, endpoints and optionally scheduled tasks that extends the server for a specific use case. + +## Types + +There are two toolkit types as of JsonDerulo API Spec v2: + +### Application + +Route prefix: `/tk/app/{toolkit}/{*path}` + +Application toolkits are full featured modules with their own UI or client (not intended to be handled by the JDE Server alone). Think of them as apps that use the server as a backend. A POS system, an inventory manager, a monitoring dashboard etc. + +### Library + +Route prefix: `/tk/lib/{toolkit}/{*path}` + +Library toolkits provide shared services or utilities. They exist to be used by application toolkits or serve as seperatable assignable permissions. Think of them as shared backend modules. + +The only functional difference is the route prefix. Everything else (permissions, endpoints, config) should work basically the same way. + +## What a toolkit can define + +- **tables**: database tables it owns. with option for the jde server to manage system columns on these and enforcing permissions +- **read_only_tables**: tables that reject all writes through the API (overwritable by powerlevel) +- **write_protected_columns**: extra columns besides system columns that cant be written by normal users +- **endpoints**: custom API routes backed by internal logic, HTTP services or executables +- **groups_table**: a database table holding toolkit specific group definitions with their own permissions +- **scheduled_queries**: SQL like queries that run periodically on a timer +- **auto_generation**: rules for auto generating field values (UUIDs, sequential codes, random strings etc) +- **system_column_overrides**: per table control over which system columns to skip + +## Config file structure + +Toolkits are one of the few which must follow a configured in TOML files. The main `toolkits.toml` sets global defaults and can define inline toolkits. Most toolkits live in individual files under the `toolkitd/` subdirectory (like `toolkitd/beepzone.toml`). + +## Enabling and disabling + +Each toolkit has an `enabled` field (default true). Disabled toolkits are completely ignored, their tables arent registered, their endpoints dont exist and their permissions arent loaded. diff --git a/toolkits/permissions.md b/toolkits/permissions.md new file mode 100644 index 0000000..e32e9d3 --- /dev/null +++ b/toolkits/permissions.md @@ -0,0 +1,68 @@ +# Toolkit Permissions + +[back to toolkits](README.md) /// [home](../README.md) + +How toolkit specific permissions work and how they get merged with core permissions. + +## Toolkit groups + +Each toolkit can have its own groups table (configured via `groups_table`). This table needs at minimum: + +| Column | Type | Notes | +|------------------------|---------|---------------------------------------------------------------------------| +| `name` | VARCHAR | group name, referenced by `jde_associations.toolkit_group_name` | +| `permissions` | JSON | array of permission rules (same format as core: `["table:rw", "logs:r"]`) | +| `endpoint_permissions` | JSON | array of allowed endpoint paths like this `["kiosk/*", "report"]` | + +## Associations + +Core groups are linked to toolkit groups via the `jde_associations` junction table. One entry per core group per toolkit: + +``` +core group "staff" (power 50) -> beepzone toolkit -> beepzone group "operators" +core group "admin" (power 100) -> beepzone toolkit -> beepzone group "managers" +``` + +When a user authenticates the server knows their core group which gives their power level and core permissions. Then for each toolkit it looks up the association to find their toolkit group and merges those permissions in. + +## Permission resolution + +At startup and on config reload the server should: + +1. read core groups from `jde_groups` +2. read toolkit groups from each toolkits `groups_table` +3. join them through `jde_associations` +4. merge the permission rules per power level +5. build the reals jsonderulo RBAC structure + +Toolkit permissions are additive to core permissions. If core gives you `r` on a table and the toolkit gives you `rw` on the same table you end up with `rw`. + +## User level overrides + +Users can have `toolkit_overrides` in their JSON preferences on `jde_users`: + +```json +[{ "toolkit": "beepzone", "group": "manager" }] +``` + +This overrides the association from their core group. So even if their core group maps to "operators" in beepzone they personally get "manager" permissions. + +## Fallback permissions + +When the database is partially fucked or the groups table doesnt exist yet the server can fall back to static permissions defined in the toolkit config and users table, they should look somewhat the same as + +```toml +[db_fallback_permissions.100] +basic_rules = ["assets:rw", "logs:r"] +advanced_rules = ["assets.secret_field:block"] +``` + +The key is the power level as a string. These only kick in when DB sourced permissions arent really available (or if the server implementation allows setting preferences as to which one wins if both are set) + +## Endpoint permissions + +Separate from table permissions. Endpoint permissions control which custom endpoint paths a user can access. These are resolved per power level from the toolkit groups `endpoint_permissions` column. + +Glob patterns are supported: `"kiosk/*"` matches all paths under kiosk. + +Endpoint fallback permissions can also be defined in the toolkit config for when the database isnt available. -- cgit v1.2.3-70-g09d2