1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.security.api;
23
24 import static com.entwinemedia.fn.Prelude.chuck;
25 import static com.entwinemedia.fn.Stream.$;
26 import static org.opencastproject.security.api.SecurityConstants.GLOBAL_ADMIN_ROLE;
27 import static org.opencastproject.security.util.SecurityUtil.getEpisodeRoleId;
28 import static org.opencastproject.util.EqualsUtil.bothNotNull;
29 import static org.opencastproject.util.EqualsUtil.eqListUnsorted;
30 import static org.opencastproject.util.data.Either.left;
31 import static org.opencastproject.util.data.Either.right;
32 import static org.opencastproject.util.data.Monadics.mlist;
33
34 import org.opencastproject.util.Checksum;
35 import org.opencastproject.util.data.Either;
36 import org.opencastproject.util.data.Function;
37 import org.opencastproject.util.data.Function2;
38 import org.opencastproject.util.data.Tuple;
39
40 import com.entwinemedia.fn.Fn;
41 import com.entwinemedia.fn.Fn2;
42 import com.entwinemedia.fn.Pred;
43 import com.entwinemedia.fn.Stream;
44 import com.entwinemedia.fn.fns.Booleans;
45
46 import org.apache.commons.lang3.StringUtils;
47
48 import java.nio.charset.StandardCharsets;
49 import java.security.MessageDigest;
50 import java.security.NoSuchAlgorithmException;
51 import java.util.ArrayList;
52 import java.util.Comparator;
53 import java.util.List;
54 import java.util.Set;
55
56
57
58
59 public final class AccessControlUtil {
60
61
62 private AccessControlUtil() {
63 }
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public static boolean isAuthorized(AccessControlList acl, User user, Organization org, Object action) {
91 return isAuthorized(acl, user, org, action, null);
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 public static boolean isAuthorized(AccessControlList acl, User user, Organization org, Object action,
122 String mediaPackageId) {
123 if (action == null || user == null || acl == null || org == null) {
124 throw new IllegalArgumentException();
125 }
126
127
128 if (user.hasRole(GLOBAL_ADMIN_ROLE) || user.hasRole(org.getAdminRole())) {
129 return true;
130 }
131
132
133 if (mediaPackageId != null && user.hasRole(getEpisodeRoleId(mediaPackageId, action.toString()))) {
134 return true;
135 }
136
137 Set<Role> userRoles = user.getRoles();
138 for (AccessControlEntry entry : acl.getEntries()) {
139 if (action.toString().equals(entry.getAction())) {
140 for (Role role : userRoles) {
141 if (role.getName().equals(entry.getRole())) {
142 return entry.isAllow();
143 }
144 }
145 }
146 }
147
148 return false;
149 }
150
151
152
153
154
155 private static Pred<Object> isAuthorizedFn(final AccessControlList acl, final User user, final Organization org) {
156 return new Pred<Object>() {
157 @Override
158 public Boolean apply(Object action) {
159 return isAuthorized(acl, user, org, action);
160 }
161 };
162 }
163
164
165
166
167
168
169 public static boolean isAuthorizedAll(AccessControlList acl, User user, Organization org, Object... actions) {
170 return !$(actions).exists(Booleans.not(isAuthorizedFn(acl, user, org)));
171 }
172
173
174
175
176
177
178 public static boolean isAuthorizedOne(AccessControlList acl, User user, Organization org, Object... actions) {
179 return $(actions).exists(isAuthorizedFn(acl, user, org));
180 }
181
182
183
184
185
186
187 public static boolean isProhibitedAll(AccessControlList acl, User user, Organization org, Object... actions) {
188 return !$(actions).exists(isAuthorizedFn(acl, user, org));
189 }
190
191
192
193
194
195
196 public static boolean isProhibitedOne(AccessControlList acl, User user, Organization org, Object... actions) {
197 return $(actions).exists(Booleans.not(isAuthorizedFn(acl, user, org)));
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213 public static AccessControlList extendAcl(AccessControlList acl, String role, String action, boolean allow) {
214 AccessControlList newAcl = new AccessControlList();
215 boolean foundAce = false;
216 for (AccessControlEntry ace : acl.getEntries()) {
217 if (ace.getAction().equalsIgnoreCase(action) && ace.getRole().equalsIgnoreCase(role)) {
218 if (ace.isAllow() == allow) {
219
220 return acl;
221 } else {
222
223 foundAce = true;
224 newAcl.getEntries().add(new AccessControlEntry(role, action, allow));
225 }
226 } else {
227 newAcl.getEntries().add(ace);
228 }
229 }
230 if (!foundAce)
231 newAcl.getEntries().add(new AccessControlEntry(role, action, allow));
232
233 return newAcl;
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247 public static AccessControlList reduceAcl(AccessControlList acl, String role, String action) {
248 AccessControlList newAcl = new AccessControlList();
249 for (AccessControlEntry ace : acl.getEntries()) {
250 if (!ace.getAction().equalsIgnoreCase(action) || !ace.getRole().equalsIgnoreCase(role)) {
251 newAcl.getEntries().add(ace);
252 }
253 }
254 return newAcl;
255 }
256
257
258
259
260
261
262
263 public static AccessControlList acl(Either<AccessControlEntry, List<AccessControlEntry>>... entries) {
264
265 final List<AccessControlEntry> seq = mlist(entries)
266 .foldl(new ArrayList<AccessControlEntry>(),
267 new Function2<List<AccessControlEntry>, Either<AccessControlEntry, List<AccessControlEntry>>, List<AccessControlEntry>>() {
268 @Override
269 public List<AccessControlEntry> apply(List<AccessControlEntry> sum,
270 Either<AccessControlEntry, List<AccessControlEntry>> current) {
271 if (current.isLeft())
272 sum.add(current.left().value());
273 else
274 sum.addAll(current.right().value());
275 return sum;
276 }
277 });
278 return new AccessControlList(seq);
279 }
280
281
282 public static Either<AccessControlEntry, List<AccessControlEntry>> entry(String role, String action, boolean allow) {
283 return left(new AccessControlEntry(role, action, allow));
284 }
285
286
287 public static Either<AccessControlEntry, List<AccessControlEntry>> entries(final String role,
288 Tuple<String, Boolean>... actions) {
289 final List<AccessControlEntry> entries = mlist(actions).map(
290 new Function<Tuple<String, Boolean>, AccessControlEntry>() {
291 @Override
292 public AccessControlEntry apply(Tuple<String, Boolean> action) {
293 return new AccessControlEntry(role, action.getA(), action.getB());
294 }
295 }).value();
296 return right(entries);
297 }
298
299
300
301
302
303
304
305
306 public static boolean equals(AccessControlList a, AccessControlList b) {
307 return bothNotNull(a, b) && eqListUnsorted(a.getEntries(), b.getEntries());
308 }
309
310
311 public static Checksum calculateChecksum(AccessControlList acl) {
312
313
314 final byte[] sep = new byte[] { 0 };
315 final MessageDigest md = $(acl.getEntries()).sort(sortAcl).bind(new Fn<AccessControlEntry, Stream<String>>() {
316 @Override
317 public Stream<String> apply(AccessControlEntry entry) {
318 return $(entry.getRole(), entry.getAction(), Boolean.toString(entry.isAllow()));
319 }
320 }).foldl(mkMd5MessageDigest(), new Fn2<MessageDigest, String, MessageDigest>() {
321 @Override
322 public MessageDigest apply(MessageDigest digest, String s) {
323 digest.update(s.getBytes(StandardCharsets.UTF_8));
324
325 digest.update(sep);
326 return digest;
327 }
328 });
329
330 try {
331 return Checksum.create("md5", Checksum.convertToHex(md.digest()));
332 } catch (NoSuchAlgorithmException e) {
333 return chuck(e);
334 }
335 }
336
337 private static MessageDigest mkMd5MessageDigest() {
338 try {
339 return MessageDigest.getInstance("MD5");
340 } catch (NoSuchAlgorithmException e) {
341 return chuck(e);
342 }
343 }
344
345 private static Comparator<AccessControlEntry> sortAcl = new Comparator<AccessControlEntry>() {
346 @Override
347 public int compare(AccessControlEntry o1, AccessControlEntry o2) {
348
349 int compareTo = StringUtils.trimToEmpty(o1.getRole()).compareTo(StringUtils.trimToEmpty(o2.getRole()));
350 if (compareTo != 0)
351 return compareTo;
352
353
354 compareTo = StringUtils.trimToEmpty(o1.getAction()).compareTo(StringUtils.trimToEmpty(o2.getAction()));
355 if (compareTo != 0)
356 return compareTo;
357
358
359 return Boolean.valueOf(o1.isAllow()).compareTo(o2.isAllow());
360 }
361 };
362
363 }