Skip to content

Commit 710c231

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

File tree

3 files changed

+369
-4
lines changed

3 files changed

+369
-4
lines changed

exist-core/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@
715715
<include>src/test/java/org/exist/xquery/functions/xmldb/XMLDBStoreTest.java</include>
716716
<include>src/test/java/org/exist/xquery/functions/xquery3/SerializeTest.java</include>
717717
<include>src/test/resources-filtered/org/exist/xquery/import-from-pkg-test.conf.xml</include>
718+
<include>src/test/xquery/securitymanager/acl.xqm</include>
718719
</includes>
719720
</licenseSet>
720721

@@ -815,6 +816,7 @@
815816
<include>src/main/java/org/exist/scheduler/impl/QuartzSchedulerImpl.java</include>
816817
<include>src/main/java/org/exist/security/EffectiveSubject.java</include>
817818
<include>src/main/java/org/exist/security/SecurityManager.java</include>
819+
<include>src/main/java/org/exist/security/SimpleACLPermission.java</include>
818820
<include>src/main/java/org/exist/security/internal/AccountImpl.java</include>
819821
<include>src/main/java/org/exist/source/Source.java</include>
820822
<include>src/main/java/org/exist/source/SourceFactory.java</include>
@@ -1027,6 +1029,7 @@
10271029
<exclude>src/main/java/org/exist/scheduler/impl/QuartzSchedulerImpl.java</exclude>
10281030
<exclude>src/main/java/org/exist/security/EffectiveSubject.java</exclude>
10291031
<exclude>src/main/java/org/exist/security/SecurityManager.java</exclude>
1032+
<exclude>src/main/java/org/exist/security/SimpleACLPermission.java</exclude>
10301033
<exclude>src/main/java/org/exist/security/internal/AccountImpl.java</exclude>
10311034
<exclude>src/main/java/org/exist/source/Source.java</exclude>
10321035
<exclude>src/main/java/org/exist/source/SourceFactory.java</exclude>
@@ -1214,6 +1217,7 @@
12141217
<exclude>src/test/java/org/exist/xquery/value/SubSequenceRangeTest.java</exclude>
12151218
<exclude>src/test/java/org/exist/xquery/value/SubSequenceTest.java</exclude>
12161219
<exclude>src/test/xquery/binary-value.xqm</exclude>
1220+
<exclude>src/test/xquery/securitymanager/acl.xqm</exclude>
12171221
<exclude>src/test/xquery/xquery3/postfix-expr.xqm</exclude>
12181222
<exclude>src/test/xquery/xquery3/serialize.xql</exclude>
12191223

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

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*
21+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
* The original license header is included below.
23+
*
24+
* =====================================================================
25+
*
226
* eXist-db Open Source Native XML Database
327
* Copyright (C) 2001 The eXist-db Authors
428
*
@@ -27,6 +51,8 @@
2751
import org.exist.storage.io.VariableByteInput;
2852
import org.exist.storage.io.VariableByteOutputStream;
2953

54+
import javax.annotation.Nullable;
55+
3056
import static org.exist.security.PermissionRequired.IS_DBA;
3157
import static org.exist.security.PermissionRequired.IS_OWNER;
3258
import static org.exist.security.PermissionRequired.ACL_WRITE;
@@ -85,10 +111,68 @@ private void addACE(final ACE_ACCESS_TYPE access_type, final ACE_TARGET target,
85111
throw new PermissionDeniedException("Maximum of " + MAX_ACL_LENGTH + " ACEs has been reached.");
86112
}
87113

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

94178
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)