ACL và Permissions — AEM 6.5 On-Premise
Phạm vi: AEM 6.5 on-premise, Java 8 (SP1+) / Java 11 (SP9+), Jackrabbit Oak
1. Permission Model — Cách ACL Hoạt Động
AEM dùng Jackrabbit Oak access control layer. Mỗi thao tác đọc, ghi, xóa trên JCR đều được kiểm soát bởi Access Control List (ACL) — map principals (user, group) với privileges trên path cụ thể.
Mỗi JCR node có thể có child node rep:policy chứa Access Control Entries (ACE):
/content/mysite/
└── rep:policy/
├── allow (rep:GrantACE)
│ ├── rep:principalName = "content-authors"
│ └── rep:privileges = ["jcr:read", "rep:write", "crx:replicate"]
└── deny (rep:DenyACE)
├── rep:principalName = "restricted-editors"
└── rep:privileges = ["jcr:removeNode"]2
3
4
5
6
7
8
Privilege reference
| Privilege | Phạm vi |
|---|---|
jcr:read | Đọc node và properties |
rep:write | Tạo, sửa, xóa node/property |
jcr:addChildNodes | Tạo child nodes |
jcr:removeChildNodes | Xóa child nodes |
jcr:removeNode | Xóa chính node đó |
jcr:modifyProperties | Chỉnh sửa properties (không tạo/xóa node) |
crx:replicate | Activate / publish content |
jcr:readAccessControl | Đọc ACL policies |
jcr:modifyAccessControl | Sửa ACL policies |
jcr:versionManagement | Tạo và restore versions |
jcr:lockManagement | Lock / unlock nodes |
jcr:all | Toàn bộ privileges (superuser) |
Thứ tự đánh giá ACE
- ACE được evaluate từ trên xuống trong
rep:policy - Deny thắng Allow tại cùng path
- ACE trên child path ghi đè parent ACE (specific wins)
- User
adminbypass toàn bộ ACL - Group
everyoneáp dụng cho mọi user đã xác thực
2. rep:glob Patterns
rep:glob giới hạn ACE chỉ áp dụng cho tập con của path bên dưới. Cho phép kiểm soát chi tiết mà không cần tạo ACE trên từng node:
/content/mysite/
└── rep:policy/
└── allow
├── rep:principalName = "content-authors"
├── rep:privileges = ["jcr:read"]
└── rep:glob = "*/jcr:content*"2
3
4
5
6
Bảng pattern thường dùng
| Pattern | Match |
|---|---|
"" (empty string) | Chỉ node đó, không bao gồm con |
* | Direct children trực tiếp |
*/jcr:content | jcr:content của direct children |
*/jcr:content* | jcr:content và toàn bộ subtree bên dưới, cho direct children |
/*/jcr:content/* | Properties/children dưới jcr:content ở depth 2 |
Ví dụ: cho đọc content nhưng không thấy page trong navigation
Path: /content/mysite/secret
rep:glob: */jcr:content*
Privilege: jcr:read (allow)2
3
User có thể đọc jcr:content (title, properties) nhưng không thấy page node secret trong tree.
3. Netcentric ACL Tool — Tiêu Chuẩn Cho AEM 6.5 On-Premise
Netcentric ACL Tool là công cụ de facto để quản lý permissions trong AEM 6.5 enterprise projects. Định nghĩa ACL bằng YAML, idempotent, version-controlled, deploy qua content package.
So sánh nhanh
| Repoinit | Netcentric ACL Tool | |
|---|---|---|
| Format | DSL trong .cfg.json | YAML |
| Group management | Basic | Đầy đủ (hierarchy, membership) |
| ACL + glob | Có | Có (+ CUG, run mode) |
| Dump existing | Không | Có (acl-dump) |
| Pre-deploy validation | Không | Có |
| AEM 6.5 | Cần config thêm | Full support |
Cài đặt vào project
<!-- pom.xml của all module -->
<dependency>
<groupId>biz.netcentric.cq.tools.accesscontroltool</groupId>
<artifactId>accesscontroltool-bundle</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>biz.netcentric.cq.tools.accesscontroltool</groupId>
<artifactId>accesscontroltool-package</artifactId>
<version>3.7.0</version>
<type>zip</type>
</dependency>2
3
4
5
6
7
8
9
10
11
12
Embed trong all package.
Định nghĩa Groups (myproject-groups.yaml)
- group_config:
- content-authors:
name: "Content Authors"
description: "Can author content on mysite"
path: /home/groups/myproject
isMemberOf: contributor
- dam-contributors:
name: "DAM Contributors"
description: "Can upload and manage assets"
path: /home/groups/myproject
- dam-publishers:
name: "DAM Publishers"
description: "Can publish assets"
path: /home/groups/myproject
isMemberOf: dam-contributors
- template-authors:
name: "Template Authors"
description: "Can create and edit editable templates"
path: /home/groups/myproject
- site-admins:
name: "Site Administrators"
description: "Full access to mysite"
path: /home/groups/myproject
isMemberOf: content-authors, dam-contributors2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Định nghĩa ACLs (myproject-acls.yaml)
- content-authors:
# Pages: đọc, viết, publish
- path: /content/mysite
permission: allow
privileges: jcr:read,rep:write,crx:replicate
# Không cho xóa top-level pages
- path: /content/mysite
permission: deny
privileges: jcr:removeNode
repGlob: ""
# DAM: chỉ đọc
- path: /content/dam/mysite
permission: allow
privileges: jcr:read
# Tags: đọc và tạo
- path: /content/cq:tags/mysite
permission: allow
privileges: jcr:read,jcr:addChildNodes,jcr:modifyProperties
# Experience Fragments
- path: /content/experience-fragments/mysite
permission: allow
privileges: jcr:read,rep:write
- dam-contributors:
- path: /content/dam/mysite
permission: allow
privileges: jcr:read,rep:write
# Yêu cầu approval workflow trước khi publish
- path: /content/dam/mysite
permission: deny
privileges: crx:replicate
- dam-publishers:
- path: /content/dam/mysite
permission: allow
privileges: jcr:read,crx:replicate
- template-authors:
- path: /conf/mysite/settings/wcm/templates
permission: allow
privileges: jcr:read,rep:write
- path: /conf/mysite/settings/wcm/policies
permission: allow
privileges: jcr:read,rep:write
- path: /content/mysite
permission: allow
privileges: jcr:read
- site-admins:
- path: /content/mysite
permission: allow
privileges: jcr:all
- path: /content/dam/mysite
permission: allow
privileges: jcr:all
- path: /content/experience-fragments/mysite
permission: allow
privileges: jcr:all
- path: /conf/mysite
permission: allow
privileges: jcr:all2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Apply ACL thủ công
# Trigger apply qua JMX
curl -u admin:admin -X POST \
"http://localhost:4502/system/console/jmx/biz.netcentric.cq.tools.accesscontroltool:type=ACTService/op/apply"2
3
Hoặc vào /system/console/actool trong Felix Console.
Dump permissions hiện tại để audit
curl -u admin:admin \
"http://localhost:4502/system/console/jmx/biz.netcentric.cq.tools.accesscontroltool:type=ACTService/op/getDump/java.lang.String" \
-d "p0=content-authors"2
3
Advanced: glob restriction trong YAML
- content-authors:
- path: /content/mysite
permission: allow
privileges: jcr:read
repGlob: "*/jcr:content*"2
3
4
5
Advanced: Conditional ACL theo run mode
# Chỉ apply trên publish instance
- content-authors:
- path: /content/mysite
permission: allow
privileges: jcr:read
runModes: publish2
3
4
5
6
4. Service Users
Service user là system principal cho OSGi service dùng để access JCR. Thay thế loginAdministrative() đã deprecated.
Tạo service user trên AEM 6.5
Cách 1 — Tạo thủ công qua CRX/DE hoặc User Admin:
- Vào
/libs/granite/security/content/useradmin - Tạo user với type
System User - Path thường dùng:
/home/users/system/myproject/
Cách 2 — Netcentric ACL Tool (system_users section):
- system_users:
- myproject-content-reader:
path: /home/users/system/myproject
- myproject-content-writer:
path: /home/users/system/myproject
- myproject-workflow-service:
path: /home/users/system/myproject
- myproject-content-reader:
- path: /content/mysite
permission: allow
privileges: jcr:read
- path: /content/dam/mysite
permission: allow
privileges: jcr:read
- myproject-content-writer:
- path: /content/mysite
permission: allow
privileges: jcr:read,rep:write
- path: /content/dam/mysite
permission: allow
privileges: jcr:read,rep:write
- myproject-workflow-service:
- path: /content/mysite
permission: allow
privileges: jcr:read,rep:write,crx:replicate
- path: /var/workflow
permission: allow
privileges: jcr:read2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Service user mapping — OSGi config
// org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~myproject.cfg.json
{
"user.mapping": [
"com.myproject.core:content-reader=myproject-content-reader",
"com.myproject.core:content-writer=myproject-content-writer",
"com.myproject.core:workflow-service=myproject-workflow-service"
]
}2
3
4
5
6
7
8
Dùng trong Java code
@Reference
private ResourceResolverFactory resolverFactory;
public void readContent() {
Map<String, Object> authInfo = Collections.singletonMap(
ResourceResolverFactory.SUBSERVICE, "content-reader"
);
try (ResourceResolver resolver =
resolverFactory.getServiceResourceResolver(authInfo)) {
Resource resource = resolver.getResource("/content/mysite/en/home");
if (resource != null) {
String title = resource.getChild("jcr:content")
.getValueMap().get("jcr:title", "");
LOG.info("Title: {}", title);
}
} catch (LoginException e) {
LOG.error("Service user login failed for subservice 'content-reader'", e);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
5. Group Hierarchy Khuyến Nghị
Nguyên tắc: least privilege — bắt đầu từ không có gì, thêm dần theo nhu cầu thực tế.
| Group | Mục đích | Permissions cốt lõi |
|---|---|---|
content-authors | Author và publish pages | read, write, replicate trên /content/mysite |
dam-contributors | Upload và quản lý assets | read, write trên /content/dam/mysite |
dam-publishers | Publish assets | read, replicate trên /content/dam/mysite |
template-authors | Tạo và quản lý editable templates | read, write trên /conf/mysite/settings/wcm/templates |
tag-administrators | Quản lý tag taxonomy | read, write trên /content/cq:tags/mysite |
site-admins | Full access cho một site | jcr:all trên /content/mysite, /content/dam/mysite |
workflow-users | Start và participate workflows | read trên /var/workflow/models |
Quy tắc thiết kế:
- Assign user vào group, không assign permission trực tiếp cho user
site-adminskế thừa từcontent-authorsvàdam-contributorsquaisMemberOf- Mỗi group một scope rõ ràng, không overlap trách nhiệm
6. Closed User Groups (CUGs)
CUG giới hạn jcr:read trên một subtree chỉ cho các principals được chỉ định. Dùng cho premium content, member-only section, intranet.
Cách CUG hoạt động
rep:cugPolicynode giới hạnjcr:readcho các principals được liệt kê- Tất cả user khác (kể cả anonymous) bị deny read
- CUG chỉ ảnh hưởng read access — write access vẫn do regular ACL kiểm soát
- CUG kế thừa xuống child nodes
Cấu hình trong Netcentric ACL Tool
- premium-members:
- path: /content/mysite/premium
permission: allow
privileges: jcr:read
# CUG config
- cug_config:
- path: /content/mysite/premium
principalNames: premium-members2
3
4
5
6
7
8
9
CUG qua JCR XML (trong ui.content package)
<jcr:root
xmlns:rep="internal"
jcr:primaryType="rep:CugPolicy"
rep:principalNames="[premium-members]"/>2
3
4
Node này đặt tại /content/mysite/premium/rep:cugPolicy.
7. Programmatic ACL — Đọc và Ghi
Đọc ACL hiện tại
import javax.jcr.Session;
import javax.jcr.security.AccessControlList;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.AccessControlPolicy;
Session session = resolver.adaptTo(Session.class);
AccessControlManager acm = session.getAccessControlManager();
AccessControlPolicy[] policies = acm.getPolicies("/content/mysite");
for (AccessControlPolicy policy : policies) {
if (policy instanceof AccessControlList) {
AccessControlList acl = (AccessControlList) policy;
for (AccessControlEntry ace : acl.getAccessControlEntries()) {
LOG.info("Principal: {}, Privileges: {}",
ace.getPrincipal().getName(),
Arrays.toString(ace.getPrivileges()));
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Ghi ACL programmatically
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
JackrabbitAccessControlManager acm =
(JackrabbitAccessControlManager) session.getAccessControlManager();
// Lấy ACL đang tồn tại hoặc tạo mới
JackrabbitAccessControlList acl = null;
for (AccessControlPolicy p : acm.getPolicies("/content/mysite")) {
if (p instanceof JackrabbitAccessControlList) {
acl = (JackrabbitAccessControlList) p;
break;
}
}
if (acl == null) {
for (AccessControlPolicy p : acm.getApplicablePolicies("/content/mysite")) {
if (p instanceof JackrabbitAccessControlList) {
acl = (JackrabbitAccessControlList) p;
break;
}
}
}
if (acl == null) {
throw new IllegalStateException("Cannot obtain ACL for /content/mysite");
}
Privilege[] privileges = {
acm.privilegeFromName("jcr:read"),
acm.privilegeFromName("rep:write")
};
acl.addAccessControlEntry(new PrincipalImpl("content-authors"), privileges);
acm.setPolicy("/content/mysite", acl);
session.save();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Lưu ý: dùng programmatic ACL trong migration script hoặc Groovy Console, không dùng trong production code thường xuyên. Quản lý ACL nên đi qua Netcentric ACL Tool.
8. Kiểm Tra Permissions
Impersonation qua UI
- Vào
/libs/granite/security/content/useradmin.html - Chọn user → Click Impersonate
- Browse site với permissions của user đó
Kiểm tra qua Groovy Console
import javax.jcr.Session
import javax.jcr.security.Privilege
def session = resourceResolver.adaptTo(Session.class)
def acm = session.accessControlManager
def checkPrivilege(String path, String privilegeName) {
def priv = [acm.privilegeFromName(privilegeName)] as Privilege[]
def result = acm.hasPrivileges(path, priv)
println "${privilegeName} on ${path}: ${result}"
}
checkPrivilege("/content/mysite", "jcr:read")
checkPrivilege("/content/mysite", "rep:write")
checkPrivilege("/content/mysite", "crx:replicate")
checkPrivilege("/content/dam/mysite", "rep:write")2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Permission checker endpoint
AEM cung cấp endpoint kiểm tra permission nhanh:
GET /system/console/jcr?path=/content/mysite&user=content-author-userKiểm tra effective permissions trong CRXDE Lite
- Mở CRXDE Lite →
/content/mysite - Tab Access Control — xem toàn bộ ACE trên path đó
- Nút Effective Permissions → nhập username → xem quyền thực tế sau khi tổng hợp cả hierarchy
9. Repoinit — Baseline Cho AEM 6.5
Repoinit không phải tiêu chuẩn chính trên AEM 6.5 như trên AEMaaCS, nhưng vẫn hoạt động. Phù hợp cho việc khởi tạo service user và ACL cơ bản trong môi trường không có Netcentric ACL Tool.
// org.apache.sling.jcr.repoinit.RepositoryInitializer~myproject.cfg.json
{
"scripts": [
"create service user myproject-content-reader with path system/myproject",
"",
"set ACL for myproject-content-reader",
" allow jcr:read on /content/mysite",
" allow jcr:read on /content/dam/mysite",
"end",
"",
"create group content-authors with path /home/groups/myproject",
"",
"set ACL for content-authors",
" allow jcr:read,rep:write,crx:replicate on /content/mysite",
" deny jcr:removeNode on /content/mysite",
" allow jcr:read on /content/dam/mysite",
"end"
]
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Repoinit syntax reference
| Statement | Ví dụ |
|---|---|
| Tạo service user | create service user my-user with path system/myproject |
| Tạo group | create group my-group with path /home/groups/myproject |
| Thêm vào group | add content-authors to group myproject-super-authors |
| Set ACL | set ACL for principal-name ... end |
| Allow | allow jcr:read on /content/mysite |
| Deny | deny rep:write on /content/mysite |
| Glob restriction | allow jcr:read on /content restriction(rep:glob,*/jcr:content*) |
| Tạo path | create path /content/mysite(sling:OrderedFolder) |
Trên AEM 6.5, ưu tiên Netcentric ACL Tool cho group management và ACL phức tạp. Dùng repoinit cho service user baseline và các path initialization.
10. Best Practices và Pitfalls Thường Gặp
Best Practices
- Mọi permission định nghĩa trong Git, deploy qua CI/CD — không chỉnh tay trong production
- Assign user vào group, không assign permission trực tiếp cho user
- Test permission changes trên dev/stage trước production
- Dùng ACL Tool dump để audit định kỳ, phát hiện drift
- Dùng
jcr:modifyPropertiesthayrep:writekhi user chỉ cần sửa nội dung, không cần tạo/xóa node
Pitfalls thường gặp
| Lỗi | Giải pháp |
|---|---|
| User không thấy page sau deploy | Kiểm tra group có jcr:read trên /content và tất cả ancestor nodes |
| ACL Tool chạy nhưng permissions không apply | Kiểm tra YAML syntax; xem log trong Felix Console /system/console/actool |
Service user bị LoginException | Kiểm tra service user mapping config và user tồn tại trong /home/users/system/ |
| Deny ACE không có hiệu lực | Deny phải nằm sau (hoặc cùng level) với allow ACE trong evaluation order |
| CUG block toàn bộ anonymous access | Kiểm tra principalNames đúng group; kiểm tra group membership của user |
rep:glob match quá rộng | Test glob cẩn thận; empty string "" chỉ match đúng node đó |
| Permissions đúng trên Author nhưng sai trên Publish | ACL phải được set cho cả publish-side groups; kiểm tra CUG có bật trên Publish |
| Repoinit script fail silent | Kiểm tra error.log sau deployment cho repoinit execution errors |
jcr:all trên /content gán cho non-admin group | Quá rộng — chỉ gán jcr:all trên site-specific path (/content/mysite) |
ACL Tool YAML dùng actions thay vì privileges | actions là legacy (CRX2), luôn dùng privileges cho Oak |
| Service user access DAM rendition nhưng thiếu read trên parent | Phải có jcr:read trên toàn bộ ancestor path từ /content/dam trở xuống |
Tham Khảo
- Netcentric ACL Tool — GitHub
- Jackrabbit Oak Access Control
- AEM 6.5 User Administration — Adobe Experience League
- Repoinit Language Reference — Apache Sling