Skip to content

Add a permission

StackRivet is default-deny: a protected endpoint is denied unless a permission allows it, and every protected endpoint must declare one (the architecture tests fail the build otherwise). The rule of thumb is declare the permission in the module manifest/permission seed first, then annotate the code.

<module>:<resource>:<action>
# e.g. customer:customer:create

A permission has a type: api (a server endpoint), button (a UI action) or menu (a navigation entry).

1. Declare it in the module manifest and seed

Section titled “1. Declare it in the module manifest and seed”

Add the permission (and a menu entry if it’s new navigation) to the module manifest or generated permission seed:

{
"permissions": [
{ "code": "customer:customer:create", "type": "button", "name": "Create customer" },
{ "code": "customer:customer:list", "type": "api", "name": "List customers" }
],
"menus": [
{ "code": "customer:customer:list", "path": "/customer/customers", "title": "Customers", "titleZh": "客户", "parent": "customer", "sortOrder": 100 }
]
}

The manifest and permission seed are the reviewable source of the module’s menu/permission surface. Community applies them through Flyway seed SQL or the admin role/menu UI; the runtime does not auto-load manifest permissions into system tables.

Annotate the endpoint with @PreAuthorize:

@Operation(summary = "Create customer")
@PostMapping
@PreAuthorize("hasAuthority('customer:customer:create')")
public R<CustomerResponse> create(@Valid @RequestBody CustomerCreateRequest request) {
return R.ok(customerService.create(request));
}

An authenticated user without the authority gets 403; an unauthenticated request gets 401.

Hide the button for users who lack the permission, using the StackRivet permission directive/component (button permissions). UI hiding is for UX only — the server check in step 2 is the real control.

A permission does nothing until a role has it and a user has the role. In the admin UI, open System → Roles, edit a role, and grant the new menu/button. Users with that role pick up the permission on their next login (permissions resolve at login).

Terminal window
# Without the authority → 403
curl -i -H "Authorization: Bearer $TOKEN" -X POST \
http://127.0.0.1:8080/api/v1/customers
# After granting the role the permission → 201/200

You can also confirm the Flyway/admin change by checking the menu tree (GET /api/v1/menus/tree) for the new entry.

Declaring a permission in the manifest/seed but forgetting @PreAuthorize leaves the endpoint unprotected — the architecture/security review catches this. Conversely, annotating with a permission code that is never seeded into the menu/permission tables means no role can be granted it. Keep the two in sync.