@nocobase/nocobase-acl-manage
Task-driven ACL governance through nb CLI for role lifecycle, global role mode, permission policy, user-role membership, and risk assessment. Use when users describe business permission outcomes instead of raw command arguments.
| name | nocobase-acl-manage |
| description | Task-driven ACL governance through nb CLI for role lifecycle, global role mode, permission policy, Portal entry access, user-role membership, and risk assessment. Use when users describe business permission outcomes instead of raw command arguments, including which roles may enter a Portal. |
| argument-hint | [task: role.*|global.role-mode.*|permission.*|user.*|risk.*] [target?] [data_source_key?] [strict_mode?] |
| allowed-tools | shell, local file reads |
| owner | platform-tools |
| version | 2.5.3 |
| last-reviewed | 2026-04-23 |
| risk-level | high |
Goal
Turn ACL and permission governance into a task-driven workflow so users can ask for business outcomes while the skill handles:
- CLI command selection and argument shaping
- capability checks and safety guards
- write confirmation and readback evidence
- risk-oriented explanation for high-impact changes
Scope
- This skill is CLI-first for reads and writes.
- Tasks are grouped into five domains:
- role
- global role mode
- permission, including Portal entry access
- user
- risk assessment
- Every write task follows
plan -> confirm -> apply -> readback. global role modeis treated as a global system policy, not a per-role field.
Non-Goals
- Do not bypass CLI by calling direct REST endpoints.
- Do not mutate ACL through ad-hoc database operations.
- Do not create temporary script files to execute ACL writes.
- Do not invoke other skills for env/plugin bootstrap; use direct
nbcommands in this skill. - Do not hide high-impact blast radius (global mode, broad snippets, broad strategy actions).
- Do not claim one-click coverage for workflows that require governance review.
Canonical Task Model
A) Role Domain
| Task | User Outcome | Required Inputs | Optional Inputs |
|---|---|---|---|
role.audit-all |
list all roles with comparable policy summary | none | data_source_key, output |
role.create-blank |
create a role with default read-only baseline (single creation mode) | role_name |
role_title, description, hidden, allow_configure, allow_new_menu, portal_uid or portal_hint when creating it for a resolved Portal |
role.compare |
explain differences between roles | role_names[] |
data_source_key, output |
B) Global Role-Mode Domain
| Task | User Outcome | Required Inputs | Optional Inputs |
|---|---|---|---|
global.role-mode.get |
read current global role mode | none | output |
global.role-mode.set |
switch global role mode | role_mode |
strict_mode |
role_mode enum mapping:
default: independent role usage (no union mode)allow-use-union: union mode available, role switching still allowedonly-use-union: force union mode for multi-role users
C) Permission Domain
| Task | User Outcome | Required Inputs | Optional Inputs |
|---|---|---|---|
permission.system-snippets.set |
set role-level system snippets | role_name, (snippet_preset or snippets) |
none |
permission.route.desktop.set |
set desktop route permissions for a role | role_name, route_ids[] |
set_mode (set or add or remove) |
permission.portal.access.set |
grant or revoke one role's access to an existing Portal | role_name, (portal_uid or portal_hint), access |
none |
permission.data-source.global.set |
set global strategy actions for all tables in one data source | role_name, global_actions[] |
data_source_key |
permission.data-source.resource.set |
set independent actions for one or more collections | role_name, (collection_hint or collection_hints[]), actions[] |
data_source_key, fields_map, scope_map, resource_scope (all by default) |
permission.scope.manage |
create/update/list reusable scopes | scope_task |
data_source_key, scope_id, scope_payload |
D) User Domain
| Task | User Outcome | Required Inputs | Optional Inputs |
|---|---|---|---|
user.assign-role |
assign one role to one or many users | role_name, (user_ids[] or user_filter) |
allow_generic_association_write |
user.unassign-role |
remove one role from one or many users | role_name, (user_ids[] or user_filter) |
allow_generic_association_write |
user.audit-role-membership |
inspect users bound to roles | (role_name or user_id) |
output |
E) Risk Domain
| Task | User Outcome | Required Inputs | Optional Inputs |
|---|---|---|---|
risk.assess-role |
risk score and rationale for one role | role_name |
data_source_key, output |
risk.assess-user |
risk score based on user-role-permission relationship | user_id |
data_source_key, output |
risk.assess-system |
system-level ACL governance risk summary | none | data_source_key, output |
Role creation interaction policy:
- always create with the same default read-only baseline (
role.create-blank) role_namemust use NocoBase role uid format withr_prefix- if user input has no
r_prefix, normalize tor_<normalized_name>and show normalized value in confirmation/readback - do not ask users to choose role archetypes (for example, "employee/auditor/manager/custom")
- if
role_nameis provided, execute creation directly - when the request includes the current resolved Portal, resolve it before confirmation; after role creation, immediately grant that role explicit entry to the Portal through
permission.portal.access.set, then read back both the role and exact Portal association - treat a failed Portal grant as partial completion and do not claim the new role is ready for that Portal
- when no Portal context is available, do not guess or grant access to an unrelated Portal; after creation succeeds, move to permission assignment guidance
- permission follow-up options: system snippets, desktop routes, Portal entry access, data-source global strategy, data-source resource strategy
Resource permission interaction policy:
- before executing
permission.data-source.resource.set, always confirm: - data source key (
mainby default unless user specifies another) - resolved target collection(s)
- action list
- data scope (
allby default when user does not specify) - disambiguate operation verbs from ACL action names:
- phrases like
add table permission/configure permissiondescribe the operation, not ACL actioncreate - only treat
createas selected action when user clearly asks for data-creation capability (can create records/can add data) - user does not need to provide exact technical collection names
- accept business names or keywords as
collection_hint(s) - resolve real collection names from the selected data source collection list
- if matching is ambiguous, present candidates and ask user to choose
- if no match is found, ask user for a clearer business keyword
- resolve scope binding before write:
- if user does not specify scope, default to
all all-> built-in scopekey=allid in target data sourceown-> built-in scopekey=ownid in target data sourcecustom-> user-specified scope id (or resolved scope key)- do not leave action scope as
nullwhen final scope isallorown - write completeness (hard rule):
- for
permission.data-source.resource.set, execute one complete write payload per target collection - the write payload must include
usingActionsConfig: true - the same payload must include the final
actions[]set, with explicit scope binding (scopeIdorscopeKey) forall|ownand explicit non-emptyfieldsarrays for field-configurable actions - do not stage writes as "set actions first, then patch fields/scope/usingActionsConfig"
- before any write, show confirmation summary (data source + resolved collections + actions + scope)
- when scope is defaulted, confirmation must explicitly state
scope=all (default)and allow user override - if any required item is missing or unresolved, ask user first and do not write
- default field rule: all fields
- full-field default must be written as explicit field-name lists resolved from target collection metadata
- do not use
fields=[]as a full-field default marker - if user did not provide field-level restrictions, apply full-field permission for each selected action
- apply full-field default to every selected action that supports field configuration (
create,view,update,export,importXlsx) - full-field default must include system fields returned by metadata (for example
sort,createdBy,createdById,updatedBy,updatedById) unless user explicitly asks to exclude them - use technical field names (
field.name) for writes, never display titles - do not auto-drop fields only because they are system/context/relation/hidden; only exclude when user intent explicitly restricts them
viewaction must default to all fields for that collection- if user explicitly asks for
all permissionson a collection, resolve runtime available actions and confirm expanded action set before write
Portal permission interaction policy:
- manage only whether a role may enter an existing custom Portal; do not treat this task as Portal-internal route, menu, page, region, control, or data permission configuration
- resolve one real enabled Portal with
nb portal list -jbefore planning a write; accept a uid, name, path, title, or business hint, but require disambiguation when matches are not unique - use the official
roles.multiPortals:list/add/removeassociation actions exposed asnb api acl roles multi-portals ...; do not write therolesMultiPortalsthrough table directly - Portal access for a union role is allowed when any effective role has the explicit Portal grant;
rootcan enter all enabled Portals allowNewMultiPortalis a separate future-Portal default policy and is not evidence that a role can enter a specific existing Portal- after a write, read back the role's Portal association and verify the exact Portal uid is present or absent
- if the Multi-portal capability or
roles multi-portalsruntime commands are unavailable, stop with capability guidance; do not emulate Portal grants in another collection
Input Contract
| Input | Required | Default | Validation | Clarification Question |
|---|---|---|---|---|
task |
yes | none | one of canonical tasks or alias | "Which ACL governance task should run?" |
role_name |
conditional | none | role exists for update/audit tasks; write tasks normalize to r_* uid |
"Which role should be targeted?" |
role_mode |
conditional | none | one of default/allow-use-union/only-use-union |
"Which global role mode should be set?" |
collection_hint / collection_hints[] |
conditional | none | required for permission.data-source.resource.set; business name/keyword input is allowed |
"Which business table(s) should be configured?" |
resolved_collection_names[] |
conditional | runtime resolved | required before write for permission.data-source.resource.set; each collection must exist in selected data source |
"I found these matching collections. Which should be used?" |
actions[] |
conditional | none | required for permission.data-source.resource.set |
"Which actions should be granted on these collections?" |
portal_uid / portal_hint |
conditional | none | required for Portal permission tasks; must resolve to one enabled custom Portal | "Which Portal should this role access?" |
access |
conditional | none | allow or deny for permission.portal.access.set |
"Should this role be allowed or denied entry to the Portal?" |
resource_scope |
no | all |
one of all / own / custom(scope_id or scope_filter) |
"Which data scope should be used? Default is all; choose own/custom only when needed." |
data_source_key |
no | main |
must exist at runtime | "Which data source key should be used? (default: main)" |
strict_mode |
no | safe |
safe or fast |
"Use safe mode with full readback?" |
allow_generic_association_write |
no | false |
boolean | "Allow guarded generic association writes for user-role assignment?" |
output |
no | text+matrix |
text, text+matrix, text+evidence |
"How detailed should the result be?" |
Default behavior when user says you decide:
- choose role tasks for role intent
- choose permission tasks for policy intent
- choose user tasks for assignment intent
- choose risk tasks for assessment intent
- for role creation intent, always use
role.create-blankfirst data_source_key=mainstrict_mode=safe
Filter Authoring Gate
Before authoring any reusable custom ACL scope, scope_filter, user_filter, or other persisted permission filter, load the nocobase-utils skill with topic filter, then read Filter Condition Format in the current task. Resolve every terminal field's live frontend interface/type and use only that group's operator allowlist. Date fields use date-specific comparison operators; $lt, $lte, $gt, and $gte remain number-only even when the backend accepts them. Do not apply this UI-displayable allowlist mechanically to fixed CLI locator objects such as --filter-by-tk or direct control filters documented by a command.
Mandatory Clarification Gate
- max clarification rounds:
2 - max questions per round:
3 - never execute writes before required inputs are complete
- for
role.create-blank, ask only forrole_namewhen missing; do not ask role-type questions - for write tasks with
role_name, normalize tor_*uid and echo normalized value in confirmation - for
permission.data-source.resource.set, defaultdata_source_key=mainwhen omitted unless user explicitly provides another data source - for
permission.data-source.resource.set, if collection hint cannot be resolved or is ambiguous, ask follow-up questions before write - for
permission.data-source.resource.set, if actions are incomplete, ask follow-up questions before write - for
permission.data-source.resource.set, if scope is omitted, apply defaultalland require confirmation before write - for
permission.data-source.resource.set, if custom scope is requested but scope id/key is unresolved, ask follow-up questions before write - for Portal writes, do not continue until one enabled custom Portal is resolved
- for
permission.data-source.resource.set, if user has not confirmed the final write plan, do not write - if user asks to set role mode for a specific role, clarify and normalize to global mode change
- if task implies writes and target identity is missing, stop and ask first
- if CLI returns auth errors (
401,403,Auth required), stop and instruct the agent to hand off environment/auth recovery tonocobase-env-manage
Workflow
- Resolve intent and normalize task.
- map aliases to canonical tasks
- map natural-language role mode wording to
default/allow-use-union/only-use-union - normalize create-role wording to
role.create-blankbaseline first, then permission assignment
- Capability gate (CLI).
- confirm direct
nbCLI is available in PATH - command assembly guard:
- command form must be
nb <command> [subcommand ...] [flags ...] - first token after
nbmust be a command (for exampleenvorapi), not a flag such as-e/-t/-j - wrong:
nb -e local - correct:
nb api resource list --resource users -e local -j - raw JSON input guard (PowerShell/runtime):
- prefer structured body flags (for example
--resources,--actions) over inline--bodywhen possible - if raw JSON body is required, prefer
--body-fileover inline--body --body-filecontent must be valid JSON encoded as UTF-8 without BOM- if inline
--bodyfails JSON parsing in PowerShell, regenerate payload as--body-fileand retry once - avoid Bash-style escaped JSON in PowerShell (for example
{\"k\":\"v\"}), it may be parsed as invalid JSON - policy payload guard (hard rule for independent resource writes):
- preflight must block
api acl roles data-source-resources create|updateandapi acl roles apply-data-permissionswhen payload is missing or invalid (--body-filepreferred,--bodycompatible) - for those writes, payload must include
usingActionsConfig: trueand non-emptyactions[] - for actions
create/view/update/export/importXlsx, each action must carry non-emptyfields[] - for every action item, scope binding must be explicit via one of:
scopeId(for explicit id binding)scopeKey(for key-based resolution, such asall/own)scope.{id|key}(compatibility readback payload)- if user intent is scope
allorown, readback must show resolved non-nullscopeId - if guard fails, stop before CLI execution and return a fixable error
- parameter safety guard:
- command shape guard for resource permissions:
roles data-source-resourcesonly hascreate|get|update; do not calllist- for
roles data-source-resources get|update, locator must be one of: --filter-by-tk <resource_config_id>--data-source-key <data_source_key> --name <collection_name>- for action-level independent-permission readback, use
roles data-source-resources get ... --appends actions - for unified independent-permission writes (single or batch collections), prefer
roles apply-data-permissions --filter-by-tk <role_name> --body-file <path> - for
roles data-sources-collections list, use--data-source-key <data_source_key>as the default locator; use--filteronly for compatibility - for collection/field resolution, prefer
nb api resource list --resource collections --filter '{}' --appends fields -jas primary metadata source - for
roles desktop-routes add, request body must be JSON array of numeric route ids - never execute write commands with uncertain, unresolved, or type-mismatched parameters
- Portal permission command guard:
- discover Portals through
nb portal list -j - require
nb api acl roles multi-portals list|add|removeto resolve from the current runtime Swagger cache - pass only the resolved Portal uid to
addorremove; do not pass a title, path, or guessed uid - perform a current-association read before the write and an immediate readback after it; an already-satisfied allow/deny request is a no-op
- lock execution base-dir before any ACL discovery/write (use one stable project root for the whole task)
- run execution guard sequence before ACL writes:
nb env listnb env update <current_env_name>nb api acl --helpnb api acl roles --help- fail-closed policy:
- if
nb api acl --helpornb api acl roles --helpfails, stop and return capability-boundary message; do not switch to ad-hoc script execution. - confirm current env context through direct CLI: run
nb env listand resolve current env from the row marked with* - if no env is configured/current, stop writes and instruct the agent to call
nocobase-env-manage, then rerun the ACL task after env recovery - if runtime command cache is missing/stale or command schema changed, run
nb env update <current_env_name> - if runtime refresh fails, stop writes and return the CLI error; instruct the agent to call
nocobase-env-managefor env/auth/runtime recovery, ornocobase-plugin-manageonly if the CLI error explicitly says a plugin must be enabled - if token is missing/invalid, stop writes and instruct the agent to call
nocobase-env-manage - resolve runtime command names via intent-to-tool-map-v1 and command help discovery
- Plan before writes.
- list proposed change set, readback checkpoints, and blast radius
- for high-impact writes require explicit confirmation
- for resource permission writes, include:
- data source key
- resolved target collection list
- action list
- scope choice (default
allwhen omitted) - resolved scope binding (
scopeId/ scope key) - field policy (
all fieldsby default unless user provided restrictions) - resolved full-field list per action when field restrictions are omitted
- Execute one task at a time.
- keep writes minimal and scoped
- prefer ACL-specific runtime commands generated from swagger
- for
permission.data-source.resource.set, prefer unified write vianb api acl roles apply-data-permissionswith completeresources[]payload; useroles data-source-resources create|updateonly as compatibility path
- Readback verification.
- verify target data changed as requested
- include concise evidence blocks
- for independent resource permissions, use
nb api acl roles data-source-resources get ... --appends actionswhen verifying action-level scope/fields - for Portal entry access, read back the exact
roleName + multiPortalUidgrant and distinguish explicit access fromallowNewMultiPortal
- Risk and boundary reporting.
- return high-impact notes even on success
- use friendly boundary messaging for unsupported paths
Tooling Policy
Primary write path:
- ACL-specific CLI runtime commands (swagger-generated)
- for user-role membership writes, prefer dedicated ACL command path first (
nb api acl roles users add/remove)
Guarded fallback path (user-role membership only):
- allowed only when dedicated ACL membership command is unavailable and
allow_generic_association_write=true - use generic
nb api resource update/listonly forusers.rolesassociation operations - mandatory readback after write
Hard restrictions:
- never use direct HTTP fallback
- never use direct database mutation
- never use generic resource commands for ACL policy writes when ACL-specific runtime commands exist
- determine ACL support by checking
nb api acl --helpandnb api acl roles --helpin the same lockedbase-dirbefore concluding capability status - never create temporary
.js/.ps1/.shexecutor scripts to bypass runtime command discovery
Safety Gate
High-impact ACL actions:
- changing global role mode
- broad system snippets (
ui.*,pm,pm.*,app) - broad data-source actions (
destroy, broad export/import grants) - role assignment to many users
Safety rules:
- keep high-impact writes behind explicit confirmation
- include blast-radius summary before apply
- always perform readback verification after write
Capability Boundary Messaging
When a scenario is not supported by current CLI/runtime/tool policy:
This scenario is currently blocked by capability or governance policy in this skill.Please complete the operation in NocoBase admin UI, or enable the guarded fallback option if available.If you want, I can provide exact UI navigation steps and field suggestions.
Verification Checklist
- task normalized to canonical task
- required inputs complete before writes
- CLI capability gate passes (env context available via direct
nb env list, runtime commands resolvable) - environment/auth prerequisites are available or explicit handoff guidance is emitted
- runtime command names resolved from command map/help
- execution guard evidence includes locked
base-dirplusnb env list,nb env update <current_env_name>,nb api acl --help, andnb api acl roles --help - every write has immediate readback evidence
- for
permission.data-source.resource.set, data source + resolved collections + actions + scope were confirmed before write - when user did not provide scope, confirmation/readback shows
allas the applied default scope - for
permission.data-source.resource.set, readback confirmsusingActionsConfig=trueand action-level scope/fields in the same write cycle - for action-level verification of independent permissions, readback command includes
--appends actions - for scope=
all|own, readback shows non-nullscopeIdand matching scope key - when field rules were omitted by user, full-field defaults were applied explicitly as non-empty field-name lists
- when full-field defaults are used, readback field lists match requested names and do not silently lose system fields
- command-level preflight blocks malformed independent-resource payloads before execution (missing/invalid
usingActionsConfig,actions, scope bindingscopeId|scopeKey,fields) roles data-source-resources get|updatelocator is explicit (filterByTkordata-source-key + name) before execution- collection/field metadata is resolved through
resource collectionsread path;roles data-sources-collections listis compatibility-only for role-facing view roles desktop-routes adduses JSON array body with numeric route ids- Portal access writes resolve one enabled Portal, use exact role/Portal keys, and have immediate readback
- no write executes with uncertain or type-mismatched parameters
- global role-mode tasks do not require
role_name - every persisted custom scope/user filter was checked against the terminal field's frontend operator group; no date field uses a number comparison operator
- boundary messages are clear and actionable
Minimal Test Scenarios
global.role-mode.getandglobal.role-mode.setwith readback.role.create-blankthen verify role exists and has conservative defaults.permission.data-source.global.setand verify strategy actions.permission.data-source.resource.setwith business collection name should auto-resolve real collection name(s) from target data source.permission.data-source.resource.setwith ambiguous collection match should ask for disambiguation before write.permission.data-source.resource.setwith missing actions should ask for clarification before write.permission.data-source.resource.setwith missing scope should default toall, show this in confirmation, and allow user override before write.permission.data-source.resource.setwithviewand no field restrictions should apply full-field permission by default via explicit non-empty field lists.permission.data-source.resource.setwith scope=allshould write explicit built-in scope binding (non-nullscopeIdfor key=all).permission.data-source.resource.setshould require pre-write confirmation including data source + resolved collections + actions + scope.permission.data-source.resource.setshould write independent policy in one complete payload (usingActionsConfig + actions + scope + fields), not multi-step patching.user.assign-rolein strict mode should use dedicated membership command when available; if unavailable, block with boundary guidance.user.assign-roleguarded fallback should run only when dedicated command is unavailable and guarded mode is explicitly enabled.risk.assess-roleshould return score + evidence + recommendations.- Full-field default should preserve system fields in readback when metadata includes them.
- Wrong base-dir or missing runtime command cache must fail-closed with boundary message, not ad-hoc script fallback.
permission.portal.access.setshould grant and revoke one explicit role/Portal relation with readback.
Reference Loading Map
| Reference | Use When | Notes |
|---|---|---|
| references/intent-presets-v1.md | intent normalization and defaults | includes global role-mode wording |
| references/intent-to-tool-map-v1.md | runtime command resolution | maps logical tasks to CLI command patterns |
| references/execution-guard-template.md | every ACL write task | fixed preflight command template for base-dir lock and fail-closed checks |
| references/result-format-v1.md | output rendering | includes risk cards and capability path |
| references/configuration.md | ACL policy details | detailed data-source and scope guidance |
| references/independent-permissions.md | resource-level permission writes | usingActionsConfig + actions + fields + scope complete-write policy |
| references/portal-access.md | granting or revoking entry to an existing Portal | official role/Portal association commands and readback |
nocobase-utils topic filter: Filter Condition Format |
authoring custom scope/user filters | load the skill first; mandatory terminal-field operator allowlist; not for fixed CLI locator/control objects |
| tests/capability-test-plan.md | capability matrix | aligned with v2 domains |
| tests/test-playbook.md | acceptance regression | prompt-first TC01, TC02, TC04-TC20 with runtime evidence commands |
| references/refactor-plan-v2.md | capability gaps and rollout plan | includes CLI migration notes |
| tests/README.md | runtime verification | playbook execution flow and reporting notes |
References
- Intent Presets v1
- Intent To Tool Map v1
- Execution Guard Template
- Result Format v1
- ACL Configuration Details
- Table Independent Permissions
- Portal Access
- Filter Condition Format
- ACL Capability Test Plan
- ACL Test Playbook
- ACL Refactor Plan v2
- ACL Capability Tests
- NocoBase ACL Handbook [verified: 2026-04-11]
Loading...
Select a file to preview
Analyzing security...
Checking scan reports and verification data.
Bill of Materials
Everything this skill can do — files, network, commands, and more.