Skip to content

Commit 3dd46be

Browse files
committed
[feature] Deduplicate contiguous entries in ACLs that target the same resource and access conditions
Closes eXist-db/exist#5138
1 parent c9e2b4d commit 3dd46be

File tree

3 files changed

+375
-4
lines changed

3 files changed

+375
-4
lines changed

exist-core/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,7 @@
10971097
<include>src/test/xquery/order.xqm</include>
10981098
<include>src/test/xquery/positional-nested.xql</include>
10991099
<include>src/test/xquery/root.xq</include>
1100+
<include>src/test/xquery/securitymanager/acl.xqm</include>
11001101
<include>src/test/xquery/securitymanager/id.xqm</include>
11011102
<include>src/test/xquery/system/as-user.xqm</include>
11021103
<include>src/test/xquery/treatAs.xq</include>
@@ -1240,6 +1241,7 @@
12401241
<include>src/main/java/org/exist/scheduler/impl/QuartzSchedulerImpl.java</include>
12411242
<include>src/main/java/org/exist/security/EffectiveSubject.java</include>
12421243
<include>src/main/java/org/exist/security/SecurityManager.java</include>
1244+
<include>src/main/java/org/exist/security/SimpleACLPermission.java</include>
12431245
<include>src/main/java/org/exist/security/internal/AccountImpl.java</include>
12441246
<include>src/main/java/org/exist/source/Source.java</include>
12451247
<include>src/main/java/org/exist/source/SourceFactory.java</include>
@@ -1498,6 +1500,7 @@
14981500
<exclude>src/main/java/org/exist/security/PermissionRequiredAspect.java</exclude>
14991501
<exclude>src/main/java/org/exist/security/SecurityManager.java</exclude>
15001502
<exclude>src/test/java/org/exist/security/SecurityManagerTest.java</exclude>
1503+
<exclude>src/main/java/org/exist/security/SimpleACLPermission.java</exclude>
15011504
<exclude>src/test/java/org/exist/security/XqueryApiTest.java</exclude>
15021505
<exclude>src/main/java/org/exist/security/internal/AccountImpl.java</exclude>
15031506
<exclude>src/main/java/org/exist/security/internal/aider/ACEAider.java</exclude>
@@ -1867,6 +1870,7 @@
18671870
<exclude>src/test/xquery/order.xqm</exclude>
18681871
<exclude>src/test/xquery/positional-nested.xql</exclude>
18691872
<exclude>src/test/xquery/root.xq</exclude>
1873+
<exclude>src/test/xquery/securitymanager/acl.xqm</exclude>
18701874
<exclude>src/test/xquery/securitymanager/id.xqm</exclude>
18711875
<exclude>src/test/xquery/system/as-user.xqm</exclude>
18721876
<exclude>src/test/xquery/treatAs.xq</exclude>

exist-core/src/main/java/org/exist/security/SimpleACLPermission.java

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
11
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* Use of this software is governed by the Business Source License 1.1
9+
* included in the LICENSE file and at www.mariadb.com/bsl11.
10+
*
11+
* Change Date: 2028-04-27
12+
*
13+
* On the date above, in accordance with the Business Source License, use
14+
* of this software will be governed by the Apache License, Version 2.0.
15+
*
16+
* Additional Use Grant: Production use of the Licensed Work for a permitted
17+
* purpose. A Permitted Purpose is any purpose other than a Competing Use.
18+
* A Competing Use means making the Software available to others in a commercial
19+
* product or service that: substitutes for the Software; substitutes for any
20+
* other product or service we offer using the Software that exists as of the
21+
* date we make the Software available; or offers the same or substantially
22+
* similar functionality as the Software.
23+
*
24+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
25+
* The original license header is included below.
26+
*
27+
* =====================================================================
28+
*
229
* eXist-db Open Source Native XML Database
330
* Copyright (C) 2001 The eXist-db Authors
431
*
@@ -27,6 +54,8 @@
2754
import org.exist.storage.io.VariableByteInput;
2855
import org.exist.storage.io.VariableByteOutputStream;
2956

57+
import javax.annotation.Nullable;
58+
3059
import static org.exist.security.PermissionRequired.IS_DBA;
3160
import static org.exist.security.PermissionRequired.IS_OWNER;
3261
import static org.exist.security.PermissionRequired.ACL_WRITE;
@@ -85,10 +114,68 @@ private void addACE(final ACE_ACCESS_TYPE access_type, final ACE_TARGET target,
85114
throw new PermissionDeniedException("Maximum of " + MAX_ACL_LENGTH + " ACEs has been reached.");
86115
}
87116

88-
final int[] newAcl = new int[acl.length + 1];
89-
System.arraycopy(acl, 0, newAcl, 0, acl.length);
90-
newAcl[newAcl.length - 1] = encodeAsACE(access_type, target, id, mode);
91-
this.acl = newAcl;
117+
// this is the final mode of all ACEs in the ACL for the targeted `id`
118+
@Nullable Integer computedAclMode = null;
119+
120+
// Walk through the existing ACE and see what the permissions are for the id
121+
for (int i = 0; i < acl.length; i++) {
122+
final ACE_TARGET aceTarget = getACETarget(i);
123+
final int aceId = getACEId(i);
124+
final int aceMode = getACEMode(i);
125+
final ACE_ACCESS_TYPE aceAccessType = getACEAccessType(i);
126+
127+
if (aceId == id && aceTarget == target) {
128+
// ids match, and existing ACE is `user` and requested addition is `user`, or existing ACE is `group` and requested addition is `group`
129+
130+
if (aceMode == mode) {
131+
// NOTE(AR) exit - we do not want to add a duplicate mode entry for same target and id
132+
return;
133+
}
134+
135+
computedAclMode = computeCumulativeAclMode(computedAclMode, aceAccessType, aceMode);
136+
}
137+
}
138+
139+
final int newMode;
140+
if (computedAclMode == null) {
141+
newMode = mode;
142+
} else {
143+
newMode = (computedAclMode ^ mode) & mode;
144+
}
145+
146+
if (newMode > 0) {
147+
final int[] newAcl = new int[acl.length + 1];
148+
System.arraycopy(acl, 0, newAcl, 0, acl.length);
149+
newAcl[newAcl.length - 1] = encodeAsACE(access_type, target, id, newMode);
150+
this.acl = newAcl;
151+
}
152+
}
153+
154+
private int computeCumulativeAclMode(@Nullable final Integer existingComputedAclMode, final ACE_ACCESS_TYPE aceAccessType, final int aceMode) {
155+
if (existingComputedAclMode == null) {
156+
return aceMode;
157+
}
158+
159+
if (aceAccessType == ACE_ACCESS_TYPE.ALLOWED) {
160+
// ALLOWED, so we set bits
161+
return existingComputedAclMode | aceMode;
162+
} else {
163+
int computedAclMode = existingComputedAclMode;
164+
// DENIED, so we unset bits
165+
if ((aceMode & READ) == READ) {
166+
// unset read bit
167+
computedAclMode = computedAclMode & 3;
168+
}
169+
if ((aceMode & WRITE) == WRITE) {
170+
// unset write bit
171+
computedAclMode = computedAclMode & 5;
172+
}
173+
if ((aceMode & EXECUTE) == EXECUTE) {
174+
// unset execute bit
175+
computedAclMode = computedAclMode & 6;
176+
}
177+
return computedAclMode;
178+
}
92179
}
93180

94181
public void insertUserACE(final int index, final ACE_ACCESS_TYPE access_type, final int userId, final int mode) throws PermissionDeniedException {

0 commit comments

Comments
 (0)