Permission Integration👑
The permission system built into generated code — three permission models, config entry points, and how to plug in your own role system.
Generated code ships with a role-based permission system split into operation, field, and data permissions. Config is centralized in permissions.json — edit config and it takes effect, no business-code changes.
Permission model
| Permission | Controls | Example |
|---|---|---|
| Operation | Who can read/insert/update/delete per table | regular users can’t delete orders |
| Field | A role hides or read-onlys certain fields | phone number hidden from support staff |
| Data | Scope visible data by ownership | can only see data you created |
Config entry point: permissions.json
Generated code contains a permissions.json structured as role → table → operation → field. A minimal example:
{
"admin": { "users": { "query": true, "create": true, "update": true, "delete": true } },
"user": { "users": { "query": true, "update": true } }
}
adminhas full access tousersusercan only query and update — not delete
Edit this file and permissions take effect immediately — that’s the smallest “config-is-code” change point.
How it’s implemented in code
Permission rules map to a per-language mechanism:
| Language | Implementation |
|---|---|
| Java (Spring Boot) | Annotations + AOP: declarative interception via @PreAuthorize, logic centralized in the framework layer |
| Go / TypeScript / Python | Permission middleware + utility functions, checked at the routing layer |
You don’t write per-table if checks — permission checks are handled uniformly by the framework layer.
Plug in your own role system
Option A: reuse the default roles, only change config
Use the roles in the generated code (e.g. admin / user) and change permissions.json to your rules.
Option B: integrate an existing user/role table If you already have a user & role system:
- Have the login state carry the current user’s role (see Login Integration)
- Map the role value to a key in
permissions.json - Permission checks read config by the mapped role
The smallest change is the “login state → role → permissions.json” chain; everything else is handled by the framework layer.
Plug in your own permission data source
If permissions.json doesn’t cover your scenario — permissions live in your own database, computed dynamically per user group, or backed by an external permission system — generated code leaves a permission data-source hook: every backend has one or two fixed decision functions that read permissions.json by default. Rewrite them and you’ve swapped the data source. Middleware, DAO, and field masking all call these functions, so changing one place rewires the whole chain.
Unified contract: (role, table, op) → { allowed, maskedFields } — whatever the data source, it only answers “can this role do this operation on this table, and which fields are masked”.
| Backend | File | Functions / methods to rewrite |
|---|---|---|
| Java (Spring Boot) | src/main/java/.../config/PermissionService.java | provide your own @Primary PermissionService bean implementing canQuery / canInsert / canUpdate / canDelete / getMaskedFields |
| Go | utils/permissionUtils.go | CheckOpAccess(role, table, op), GetMaskedFields(role, table, op) |
| TypeScript | src/auth/permissions.ts | checkOpPermission(role, table, op), getMaskedFields(role, table, op) |
| Python | auth/permissions.py | check_op_permission(role, table, op), get_masked_fields(role, table, op) |
Java runs on the Spring container, and
PermissionServiceis itself a bean — if you’d rather not touch generated files, just add a same-named bean marked@Primaryin your own code to override it; Go / TypeScript / Python: edit the functions in the matching file directly.
Three ways to integrate
- Full source swap: rewrite the decision functions to query your own permission tables / user groups / external API
- Extend the default config: in the decision function, first compute against
permissions.json, then merge your own source (e.g. whatever isn’t denied by default is then decided by your tables) - Heterogeneous mode: your role model differs from the defaults — just keep the contract; how roles are defined and how role ↔ permission maps is up to you
Minimal example (Go, querying your own permission table)
// Rewrite CheckOpAccess to use your own permission table as the source
func CheckOpAccess(role, table, op string) bool {
var allowed bool
// Swap in your own query (illustrative; db is your connection)
err := db.QueryRow(
"SELECT COALESCE(MAX(allowed), false) FROM sys_role_permission WHERE role=$1 AND tbl=$2 AND op=$3",
role, table, op,
).Scan(&allowed)
if err != nil { return false } // not found → deny (change to allow-by-default if you prefer)
return allowed
}
Every backend permission file has the same “hook point” comment at the top — open the code and you’ll see which functions to change. Regeneration overwrites these files — manage your changes with git and merge after regenerating (see FAQ).
Field & data permissions
- Field permissions: specify in config that a role hides or read-onlys certain fields on a table; the response layer masks automatically
- Data permissions: used together with data ownership (see Advanced Settings), filtering query results by ownership
Notes
- Function-permission configuration is done in the workbench’s Function Permission area (a subscription feature); the generated
permissions.jsonis the landed result - Code generated from the free/basic tier contains placeholder permission logic; full permissions require generation under a subscription