|
1 | 1 | /*
|
| 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 | + * |
2 | 26 | * eXist-db Open Source Native XML Database
|
3 | 27 | * Copyright (C) 2001 The eXist-db Authors
|
4 | 28 | *
|
|
27 | 51 | import org.exist.storage.io.VariableByteInput;
|
28 | 52 | import org.exist.storage.io.VariableByteOutputStream;
|
29 | 53 |
|
| 54 | +import javax.annotation.Nullable; |
| 55 | + |
30 | 56 | import static org.exist.security.PermissionRequired.IS_DBA;
|
31 | 57 | import static org.exist.security.PermissionRequired.IS_OWNER;
|
32 | 58 | 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,
|
85 | 111 | throw new PermissionDeniedException("Maximum of " + MAX_ACL_LENGTH + " ACEs has been reached.");
|
86 | 112 | }
|
87 | 113 |
|
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 | + } |
92 | 176 | }
|
93 | 177 |
|
94 | 178 | public void insertUserACE(final int index, final ACE_ACCESS_TYPE access_type, final int userId, final int mode) throws PermissionDeniedException {
|
|
0 commit comments