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