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 manifest 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).

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

{
"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 is the source of truth: its permission and menu seeds load into the system tables on startup.

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 seed loaded by checking the menu tree (GET /api/v1/menus/tree) for the new entry.

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