Permissions
Contents
Every content item carries an access-control list (ACL) that determines which principals may read, modify, publish, and otherwise act on it.
Permission types
A content item grants seven independent permissions, each enabling a specific operation:
-
READ -
Retrieve the content item and its properties. Without READ, the item is invisible to the caller — queries skip it, direct fetches return "not found".
-
CREATE -
Create child content directly under this item.
-
MODIFY -
Edit the item’s data, displayName, or any property other than its ACL.
-
DELETE -
Remove the item from the project.
-
PUBLISH -
Push the item from
drafttomaster(or unpublish in the reverse direction). -
READ_PERMISSIONS -
Read the ACL of this item.
-
WRITE_PERMISSIONS -
Change the ACL of this item.
The seven permissions are independent — granting MODIFY does not imply READ, though in practice ACLs are written to grant logical bundles.
Principals
Permissions are granted to principals — actors known to the IAM system. Three principal types are recognised in ACL entries:
-
user:<idprovider>:<username> -
A specific user, e.g.
user:default:alice. -
group:<idprovider>:<groupname> -
A group of users; granting to a group grants to every member.
-
role:<rolename> -
A role; granting to a role grants to every user holding it, directly or transitively.
Matching is transitive — when XP evaluates whether a caller may act on a content item, it walks every group the caller belongs to and every role they hold (including via group membership) and unions the granted permissions.
Built-in roles relevant to CMS
-
role:system.everyone -
Any caller, including unauthenticated/anonymous requests. Granting
READto this role makes a content item publicly readable. -
role:system.authenticated -
Any signed-in caller.
-
role:system.admin -
Platform-level superuser; bypasses content ACLs.
-
role:cms.admin -
CMS-wide admin; full access across all projects.
-
role:cms.expert -
CMS developer-level access.
-
role:cms.user -
Baseline role for any user of Content Studio.
Each project also defines its own roles (Owner / Editor / Author / Contributor / Viewer), registered under principal IDs of the form role:com.enonic.cms.project.<id>.<roletype>.
ACL model
Each ACL entry lists permissions granted to one principal. A caller has a permission when at least one matching entry grants it.
At the Node API level, the content item’s underlying node exposes its ACL as _permissions. The following excerpt shows granted permissions in that representation. It is not the physical storage format or a GraphQL response; Guillotine exposes the permissions field instead.
{
"_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"_path": "/my-site/articles/my-article",
"_permissions": [
{
"principal": "role:system.everyone",
"allow": ["READ"]
},
{
"principal": "role:com.enonic.cms.project.myproject.author",
"allow": ["READ", "CREATE", "MODIFY", "DELETE"]
},
{
"principal": "role:com.enonic.cms.project.myproject.owner",
"allow": ["READ", "CREATE", "MODIFY", "DELETE", "PUBLISH", "READ_PERMISSIONS", "WRITE_PERMISSIONS"]
}
]
}
The ACL is system metadata associated with the node, separate from the editorial fields in data. For the underlying storage model, see the XP permissions reference.
Inheritance: copy-on-create
When a content item is created, it receives a copy of its parent’s ACL as its initial state. From that point on the two ACLs are independent — changes to the parent’s ACL do not propagate.
There is no live inheritance and no inheritPermissions flag. The flag from earlier XP versions was removed in XP 8 in favour of explicit recursive updates. To roll an ACL change down a subtree, use the apply operation described below. |
Applying an ACL change to descendants
To update an ACL across an existing subtree, callers explicitly invoke the recursive apply operation. This walks every descendant under a given path and replaces (or merges) their ACLs in one administrative pass. Editors trigger this via Content Studio’s permission dialog ("Apply to children"); programmatic callers use the corresponding lib-content function.
See the permissions reference in the Enonic Development Kit for the function signatures and merge semantics.
Query semantics
Every query is implicitly filtered by the caller’s READ permission. Items the caller cannot read are removed before hits, total counts, and aggregation buckets are returned. The same query run anonymously, as an editor, or as a system admin produces different result sets — see Content Querying for the broader implication.
XP also indexes each ACL into per-permission system fields so you can query about permissions:
_permissions_read, _permissions_create, _permissions_modify, _permissions_delete, _permissions_publish, _permissions_readpermissions, _permissions_writepermissions
Each field contains the list of principals that ultimately hold the corresponding permission on the item. Example: find every item modifiable by a given project’s editor role:
{
"term": {
"field": "_permissions_modify",
"value": { "string": "role:com.enonic.cms.project.myproject.editor" }
}
}
The indexed fields support permission-aware queries; they do not bypass the implicit read filter — the caller still only sees items they themselves have READ on.
Querying via GraphQL
Guillotine exposes the ACL on the Content interface via permissions, without a leading underscore. There is no _permissions GraphQL field. The example below retrieves ACL entries for an editorial UI that needs to show the permissions granted to each principal.
{
guillotine(project: "myproject", branch: "master") {
get(key: "/my-site/articles/my-article") {
_id
displayName
permissions {
permissions {
principal { value }
allow
}
}
}
}
}
The outer permissions object contains the ACL entries in its permissions list. Each entry identifies a principal; allow is the API field containing the permissions granted to that principal. See the Guillotine permissions reference for the full field definitions.
Where permissions come from
For a freshly-created content item, the initial ACL is the copy of the parent’s ACL — and ultimately the project’s root item, whose ACL is shaped by the project’s access mode and granted project roles. See Content projects for the project-level model.
For layered projects, each layer carries its own ACLs — see the layering page for inheritance behaviour between parent and child layers.
See also
-
XP platform docs — permissions model for the underlying node-level mechanics shared across all XP storage, not just CMS content.
-
Enonic Development Kit — permissions for the
lib-content/lib-nodefunction signatures used to read and modify ACLs programmatically.