一键导入
jmix-create-resource-role
Create or update Jmix resource roles with entity, attribute, view, and menu policies, including the CREATE-implies-MODIFY rule.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create or update Jmix resource roles with entity, attribute, view, and menu policies, including the CREATE-implies-MODIFY rule.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Open a Jmix entity detail view from a button/action and refresh related data after save.
Add Jmix entity lifecycle event listeners to perform additional actions with saved or loaded entities.
Add complete Jmix message keys for entities, enums, views, actions, and validation text.
Configure or audit Jmix fetch plans in XML views, fragments, DataManager loads, repositories, and entity events when loading references, avoiding N+1 queries, fixing unfetched attribute errors, or tuning data loading.
Add inline editable parent-child composition UI to a Jmix detail view, when a parent entity owns child records edited via a property-bound collection container (no query loader).
Create a Jmix Flow UI detail view with XML descriptor, save-close action, messages, and policies.
| name | jmix-create-resource-role |
| description | Create or update Jmix resource roles with entity, attribute, view, and menu policies, including the CREATE-implies-MODIFY rule. |
Use this skill when adding or changing Jmix security access.
The model is ADDITIVE / no-deny: if any assigned role grants access the user has
it, and there is no deny-policy. A role interface may extend several role
interfaces of the SAME kind to compose their policies (a role cannot mix
@ResourceRole and @RowLevelRole).
If a role grants EntityPolicyAction.CREATE on an entity, that entity's
@EntityAttributePolicy for the editable fields MUST be
EntityAttributePolicyAction.MODIFY — NEVER VIEW. This holds even when the
entity is immutable after creation (READ + CREATE only, no UPDATE/DELETE):
VIEW is read-only, so a user with CREATE + VIEW can persist a row but cannot
fill its fields. At runtime an attribute with only a VIEW policy is modify-denied
(EntityAttributeContext.canModify() returns false), so a CREATE + VIEW user gets a
create form whose fields are all read-only — unusable.
// CORRECT for an immutable-after-create entity:
@EntityAttributePolicy(entityClass = <Entity>.class,
attributes = "*", action = EntityAttributePolicyAction.MODIFY) // MODIFY, not VIEW
@EntityPolicy(entityClass = <Entity>.class,
actions = { EntityPolicyAction.READ, EntityPolicyAction.CREATE })
void entityPolicy();
Apply this to EVERY role consistently. A frequent mistake is getting the manager
role right (MODIFY) but leaving a read/employee role on VIEW while it still has
CREATE — both must be MODIFY if both have CREATE. If some attributes must be
read-only even at creation (e.g. auto-generated audit fields), exclude them from the
attributes list or use a second VIEW policy for that subset — NOT a blanket
VIEW on *.
Map the EXACT wording of the requirement to entity-policy actions. Re-read the requirement for the entity BEFORE writing the policy block.
| Requirement wording (about an entity) | EntityPolicyAction |
|---|---|
| "view only", "read only" | READ |
| "view and create", "create only" | READ, CREATE |
| "view, edit" | READ, UPDATE |
| "view, create, delete" (no update) | READ, CREATE, DELETE |
| "full CRUD", "manage", "all operations" | ALL |
| "cannot be updated", "immutable" | do NOT include UPDATE |
| "cannot be deleted" | do NOT include DELETE |
| "cannot be updated or deleted" | READ, CREATE only — even for managers |
For an entity described as "cannot be updated or deleted", the role must NOT
grant UPDATE or DELETE — not even for the manager role.
EntityPolicyAction.ALL includes both and contradicts the "cannot be updated or deleted" requirement. Note
list_read is an open MODE (read-only access), not a readOnly descriptor.
For every entity that appears as a composition property on another entity, the
child's detail view is opened as a DIALOG from the parent's detail view. That
dialog is a real Jmix view subject to @ViewPolicy, even though users never reach
it from a menu — so it has no @MenuPolicy entry. Always include the child detail
view id in @ViewPolicy for every role that can edit the parent.
@ViewPolicy(viewIds = {
"<Parent>.list",
"<Parent>.detail",
"<Child>.detail", // composition child, opened from <Parent>.detail
})
@MenuPolicy(menuIds = {"<Parent>.list"}) // child detail is NOT in menu
void parentScreens();
A @ViewPolicy that lists parent list+detail but omits the child detail will pass
compilation and fail at runtime when the user clicks "+" inside the parent's
composition table.
Row-level roles are a separate first-class concept from resource roles: a resource
role grants what you can do, a row-level role restricts which rows you see. They
live in their own interface annotated with @RowLevelRole and never mix with
@ResourceRole.
@JpqlRowLevelPolicy(entityClass = ..., where = "...") filters at the database
level. Use {E} as the entity alias and :current_user_* params (e.g.
:current_user_username).@PredicateRowLevelPolicy(entityClass = ..., actions = {...}) filters in-memory;
the method returns a RowLevelPredicate / RowLevelBiPredicate. Use for logic
that JPQL cannot express and for non-read operations.Gotcha: a JPQL policy only affects the root entity of a loaded graph. If the same
entity is also loaded as a collection inside another entity's graph, define BOTH
a @JpqlRowLevelPolicy and a @PredicateRowLevelPolicy for it to keep access
consistent.
@RowLevelRole(name = "Own Orders Only", code = "app_OwnOrdersOnly")
public interface OwnOrdersOnlyRole {
@JpqlRowLevelPolicy(entityClass = Order.class,
where = "{E}.createdBy = :current_user_username")
void orderPolicy();
}
A clean compile is NOT "done" — most role defects survive compileJava and surface
only at render or in tests. Confirm these with concrete checks, not by re-reading
your own code:
EntityPolicyAction.CREATE and confirm its @EntityAttributePolicy action on
the editable attributes is MODIFY, not VIEW. A CREATE paired with a VIEW
attribute policy leaves every create-form field read-only at runtime — a common, easy-to-miss mistake.@MenuPolicy uses LEAF ids, not the group id. Read menu.xml. The ids must
be the exact <item> ids/views the user opens; granting the enclosing
<menu id="..."> GROUP id does NOT grant its items.@ViewPolicy entry. Include composition-dialog
detail views opened from a parent grid even though they have no menu item.To assert a permission in Java at runtime, inject AccessManager and call
applyRegisteredConstraints(...) on a context (e.g. EntityOperationContext), then
check isPermitted().
menu.xml, not parent grouping ids.MODIFY attribute access even when update/delete are forbidden.import io.jmix.security.model.EntityAttributePolicyAction;
import io.jmix.security.model.EntityPolicyAction;
import io.jmix.security.role.annotation.EntityAttributePolicy;
import io.jmix.security.role.annotation.EntityPolicy;
import io.jmix.security.role.annotation.ResourceRole;
import io.jmix.securityflowui.role.annotation.MenuPolicy;
import io.jmix.securityflowui.role.annotation.ViewPolicy;
@ResourceRole(name = "Employee", code = EmployeeRole.CODE)
public interface EmployeeRole {
String CODE = "employee";
@EntityAttributePolicy(entityClass = OrderRequest.class,
attributes = "*",
action = EntityAttributePolicyAction.MODIFY)
@EntityPolicy(entityClass = OrderRequest.class,
actions = {EntityPolicyAction.READ, EntityPolicyAction.CREATE})
void orderRequestEntity();
@ViewPolicy(viewIds = {
"OrderRequest.list",
"OrderRequest.detail"
})
@MenuPolicy(menuIds = "OrderRequest.list")
void orderRequestScreens();
}
| Surface | Required? | Policy |
|---|---|---|
| Entity CRUD | yes | @EntityPolicy |
| Entity attributes | yes | @EntityAttributePolicy |
| List view | if user opens it | @ViewPolicy |
| Detail/dialog view | if user opens it | @ViewPolicy |
| Menu item | if user opens it from menu | @MenuPolicy |
Menu groups are navigation containers, not the user-facing item policies most roles
need. Read menu.xml and grant the item ids that open views.
<menu id="sales" title="msg://menu.sales">
<item view="Customer.list"/>
<item id="orders" view="Order.list"/>
</menu>
@MenuPolicy(menuIds = {"Customer.list", "orders"})
Do not grant only sales unless the role is intentionally controlling the parent
group itself and the project's security checks use that group id.
EntityPolicyAction.ALL when update/delete are not required.UPDATE or DELETE entity actions for immutable or create-only records.MODIFY attribute permission for editable fields.