001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.core.right; 017 018import java.util.Collection; 019import java.util.Collections; 020import java.util.Comparator; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Optional; 026import java.util.Set; 027import java.util.stream.Collectors; 028 029import org.apache.commons.collections.CollectionUtils; 030import org.apache.commons.lang3.tuple.Pair; 031 032import org.ametys.core.group.GroupIdentity; 033import org.ametys.core.right.AccessController.AccessResult; 034import org.ametys.core.right.ProfileAssignmentStorage.AnonymousOrAnyConnectedKeys; 035import org.ametys.core.right.ProfileAssignmentStorage.UserOrGroup; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.runtime.plugin.ExtensionPoint; 038import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint; 039 040/** 041 * {@link ExtensionPoint} handling {@link ProfileAssignmentStorage}s. 042 */ 043public class ProfileAssignmentStorageExtensionPoint extends AbstractThreadSafeComponentExtensionPoint<ProfileAssignmentStorage> 044{ 045 /** Avalon Role */ 046 public static final String ROLE = ProfileAssignmentStorageExtensionPoint.class.getName(); 047 048 /* ---------- */ 049 /* PUBLIC API */ 050 /* ---------- */ 051 052 /** 053 * Gets the permissions a user has, given some groups and profiles, on an object. 054 * @param user The user 055 * @param userGroups The groups 056 * @param profileIds The ids of the profiles 057 * @param object The object 058 * @return the permissions a user has, given some groups and profiles on an object. 059 */ 060 public Map<String, AccessResult> getPermissions(UserIdentity user, Set<GroupIdentity> userGroups, Set<String> profileIds, Object object) 061 { 062 getLogger().debug("Try to determine permissions for user '{}' and groups {} on context {} for profiles [{}]", user, userGroups, object, profileIds); 063 064 Map<String, AccessResult> results = new HashMap<>(); 065 066 // 1) First initialize the access results with the allowed profiles for Anonymous 067 Map<AnonymousOrAnyConnectedKeys, Set<String>> profilesForAnonymousAndAnyConnectedUser = getProfilesForAnonymousAndAnyConnectedUser(object); 068 069 Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_ALLOWED)).orElse(Set.of()) 070 .stream() 071 .filter(profileIds::contains) 072 .forEach(p -> results.putIfAbsent(p, AccessResult.ANONYMOUS_ALLOWED)); 073 074 if (results.size() == profileIds.size()) 075 { 076 // Stop process here if access were determined for all profiles 077 _logResult(user, userGroups, object, results); 078 return results; 079 } 080 081 // 2) Add to access results the denied profiles for user (among the still undetermined profiles) 082 Map<UserIdentity, Map<UserOrGroup, Set<String>>> profilesForUsers = getProfilesForUsers(object, user); 083 Optional.ofNullable(profilesForUsers.get(user)).map(a -> a.get(UserOrGroup.DENIED)).orElse(Set.of()) 084 .stream() 085 .filter(profileIds::contains) 086 .forEach(p -> results.putIfAbsent(p, AccessResult.USER_DENIED)); 087 088 if (results.size() == profileIds.size()) 089 { 090 // Stop process here if access were determined for all profiles 091 _logResult(user, userGroups, object, results); 092 return results; 093 } 094 095 // 3) Add to access results the allowed profiles for user (among the still undetermined profiles) 096 Optional.ofNullable(profilesForUsers.get(user)).map(a -> a.get(UserOrGroup.ALLOWED)).orElse(Set.of()) 097 .stream() 098 .filter(profileIds::contains) 099 .forEach(p -> results.putIfAbsent(p, AccessResult.USER_ALLOWED)); 100 101 if (results.size() == profileIds.size()) 102 { 103 // Stop process here if access were determined for all profiles 104 _logResult(user, userGroups, object, results); 105 return results; 106 } 107 108 // 4) Add to access results the denied profiles for group (among the still undetermined profiles) 109 Map<GroupIdentity, Map<UserOrGroup, Set<String>>> profilesForGroups = getProfilesForGroups(object, userGroups); 110 for (GroupIdentity userGroup : userGroups) 111 { 112 Optional.ofNullable(profilesForGroups.get(userGroup)).map(a -> a.get(UserOrGroup.DENIED)).orElse(Set.of()) 113 .stream() 114 .filter(profileIds::contains) 115 .forEach(p -> results.putIfAbsent(p, AccessResult.GROUP_DENIED)); 116 } 117 118 if (results.size() == profileIds.size()) 119 { 120 // Stop process here if access were determined for all profiles 121 _logResult(user, userGroups, object, results); 122 return results; 123 } 124 125 // 5) Add to access results the allowed profiles for group (among the still undetermined profiles) 126 for (GroupIdentity userGroup : userGroups) 127 { 128 Optional.ofNullable(profilesForGroups.get(userGroup)).map(a -> a.get(UserOrGroup.ALLOWED)).orElse(Set.of()) 129 .stream() 130 .filter(profileIds::contains) 131 .forEach(p -> results.putIfAbsent(p, AccessResult.GROUP_ALLOWED)); 132 } 133 134 if (results.size() == profileIds.size()) 135 { 136 // Stop process here if access were determined for all profiles 137 _logResult(user, userGroups, object, results); 138 return results; 139 } 140 141 // 6) Add to access results the denied profiles for any connected user (among the still undetermined profiles) 142 Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_DENIED)).orElse(Set.of()) 143 .stream() 144 .filter(profileIds::contains) 145 .forEach(p -> results.putIfAbsent(p, AccessResult.ANY_CONNECTED_DENIED)); 146 147 if (results.size() == profileIds.size()) 148 { 149 // Stop process here if access were determined for all profiles 150 _logResult(user, userGroups, object, results); 151 return results; 152 } 153 154 // 7) Add to access results the allowed profiles for any connected user (among the still undetermined profiles) 155 Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_ALLOWED)).orElse(Set.of()) 156 .stream() 157 .filter(profileIds::contains) 158 .forEach(p -> results.putIfAbsent(p, AccessResult.ANY_CONNECTED_ALLOWED)); 159 160 if (results.size() == profileIds.size()) 161 { 162 // Stop process here if access were determined for all profiles 163 _logResult(user, userGroups, object, results); 164 return results; 165 } 166 167 // 8) Add to access results the denied profiles for any connected user (among the still undetermined profiles) 168 Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED)).orElse(Set.of()) 169 .stream() 170 .filter(profileIds::contains) 171 .forEach(p -> results.putIfAbsent(p, AccessResult.ANONYMOUS_DENIED)); 172 173 if (results.size() == profileIds.size()) 174 { 175 // Stop process here if access were determined for all profiles 176 _logResult(user, userGroups, object, results); 177 return results; 178 } 179 180 // 9) Finally, add to access results the profiles for which no access could be determined 181 for (String profileId : profileIds) 182 { 183 results.putIfAbsent(profileId, AccessResult.UNKNOWN); 184 } 185 186 _logResult(user, userGroups, object, results); 187 return results; 188 } 189 190 private void _logResult(UserIdentity user, Set<GroupIdentity> userGroups, Object object, Map<String, AccessResult> accessResultsByProfile) 191 { 192 getLogger().debug("Access result found for {} and groups {} on context '{}' are {}", user, userGroups, object, accessResultsByProfile); 193 } 194 195 /** 196 * Returns some profiles that are matching if the user has a permission on at least one object, given some groups and profiles 197 * @param rootContexts The root contexts object where to seek 198 * @param user The user 199 * @param userGroups The groups 200 * @param profileIds The ids of the profiles 201 * @return If the Set is empty, it means any connected user has no matching profile.<br> 202 * If the Set is non empty, it contains at least one of the given profile BUT it may not contains all the matching profiles for anyconnected user AND it can contains some other profiles that were not in the given profiles 203 */ 204 public Set<String> hasUserAnyPermission(Set<? extends Object> rootContexts, UserIdentity user, Set<GroupIdentity> userGroups, Set<String> profileIds) 205 { 206 getLogger().debug("Try to determine permissions on any context for user '{}' and groups {} with profiles {}", user, userGroups, profileIds); 207 208 List<ProfileAssignmentStorage> sortedPas = getExtensionsIds().stream() 209 .map(this::getExtension) 210 .sorted(Comparator.comparing(ProfileAssignmentStorage::getPriority)) 211 .collect(Collectors.toList()); 212 213 Set<? extends Object> remainingRootsToTest = new HashSet<>(rootContexts); 214 for (ProfileAssignmentStorage profileAssignmentStorage : sortedPas) 215 { 216 if (!remainingRootsToTest.isEmpty()) 217 { 218 Set<? extends Object> filteredContexts = remainingRootsToTest.stream().filter(profileAssignmentStorage::isRootContextSupported).collect(Collectors.toSet()); 219 if (!filteredContexts.isEmpty()) 220 { 221 Set<String> hasUserAnyPermission = _hasUserAnyPermission(profileAssignmentStorage, filteredContexts, user, userGroups, profileIds); 222 if (!hasUserAnyPermission.isEmpty()) 223 { 224 getLogger().debug("Find permission on any context for user '{}' and groups {} with profiles {}", user, userGroups, profileIds); 225 return hasUserAnyPermission; 226 } 227 } 228 // Remove the already supported contexts 229 remainingRootsToTest.removeAll(filteredContexts); 230 } 231 } 232 233 getLogger().debug("Find no permission on any context for user '{}' and groups {} with profiles {}", user, userGroups, profileIds); 234 return Set.of(); 235 } 236 237 private Set<String> _hasUserAnyPermission(ProfileAssignmentStorage profileAssignmentStorage, Set<? extends Object> rootContexts, UserIdentity user, Set<GroupIdentity> userGroups, Set<String> profileIds) 238 { 239 if (profileIds.isEmpty() || rootContexts.isEmpty()) 240 { 241 return Set.of(); 242 } 243 244 245 // 1) Search at least one profile in "allowed-anonymous-profiles" 246 Set<String> hasAnonymousAnyAllowedProfile = profileAssignmentStorage.hasAnonymousAnyAllowedProfile(rootContexts, profileIds); 247 if (!hasAnonymousAnyAllowedProfile.isEmpty()) 248 { 249 return hasAnonymousAnyAllowedProfile; 250 } 251 252 // 2) Search at least one profile in "allowed-profiles" for user 253 Set<String> hasUserAnyAllowedProfile = profileAssignmentStorage.hasUserAnyAllowedProfile(rootContexts, user, profileIds); 254 if (!hasUserAnyAllowedProfile.isEmpty()) 255 { 256 return hasUserAnyAllowedProfile; 257 } 258 259 // 3) Search at least one profile in "allowed-profiles" for groups 260 Set<String> hasGroupAnyAllowedProfile = profileAssignmentStorage.hasGroupAnyAllowedProfile(rootContexts, userGroups, profileIds); 261 if (!hasGroupAnyAllowedProfile.isEmpty()) 262 { 263 return hasGroupAnyAllowedProfile; 264 } 265 266 // 4) Search at least one profile in "allowed-any-connected-profiles" 267 Set<String> hasAnyConnectedAnyAllowedProfile = profileAssignmentStorage.hasAnyConnectedAnyAllowedProfile(rootContexts, profileIds); 268 if (!hasAnyConnectedAnyAllowedProfile.isEmpty()) 269 { 270 return hasAnyConnectedAnyAllowedProfile; 271 } 272 273 // 5) Not found, return nothing 274 return Set.of(); 275 } 276 277 /** 278 * Returns some profiles that are matching if anybody has a permission on at least one object, given some profiles 279 * @param rootContexts The root contexts object where to seek 280 * @param profileIds The ids of the profiles 281 * @return If the Set is empty, it means anonymous has no matching profile.<br> 282 * If the Set is non empty, it contains at least one of the given profile BUT it may not contains all the matching profiles for anonymous AND it can contains some other profiles that were not in the given profiles 283 */ 284 public Set<String> hasAnonymousAnyPermission(Set<? extends Object> rootContexts, Set<String> profileIds) 285 { 286 getLogger().debug("Try to determine permissions on any context for anonymous with profiles {}", profileIds); 287 288 List<ProfileAssignmentStorage> sortedPas = getExtensionsIds().stream() 289 .map(this::getExtension) 290 .sorted(Comparator.comparing(ProfileAssignmentStorage::getPriority)) 291 .collect(Collectors.toList()); 292 293 Set<? extends Object> remainingRootsToTest = new HashSet<>(rootContexts); 294 for (ProfileAssignmentStorage profileAssignmentStorage : sortedPas) 295 { 296 if (!remainingRootsToTest.isEmpty()) 297 { 298 Set<? extends Object> filteredContexts = remainingRootsToTest.stream().filter(profileAssignmentStorage::isRootContextSupported).collect(Collectors.toSet()); 299 if (!filteredContexts.isEmpty()) 300 { 301 Set<String> hasAnonymousAnyPermission = _hasAnonymousAnyPermission(profileAssignmentStorage, filteredContexts, profileIds); 302 if (!hasAnonymousAnyPermission.isEmpty()) 303 { 304 getLogger().debug("Find permission on any context for anonymous with profiles {}", profileIds); 305 return hasAnonymousAnyPermission; 306 } 307 } 308 // Remove the already supported contexts 309 remainingRootsToTest.removeAll(filteredContexts); 310 } 311 } 312 313 getLogger().debug("Find no permission on any context for anonymous with profiles {}", profileIds); 314 return Set.of(); 315 } 316 317 private Set<String> _hasAnonymousAnyPermission(ProfileAssignmentStorage profileAssignmentStorage, Set< ? extends Object> rootContexts, Set<String> profileIds) 318 { 319 if (profileIds.isEmpty() || rootContexts.isEmpty()) 320 { 321 return Set.of(); 322 } 323 324 // Search at least one profile in "allowed-anonymous-profiles", if found return true 325 return profileAssignmentStorage.hasAnonymousAnyAllowedProfile(rootContexts, profileIds); 326 } 327 328 /** 329 * Returns some profiles that are matching if any connected user has a permission on at least one object, given some profiles 330 * @param rootContexts The root contexts object where to seek 331 * @param profileIds The ids of the profiles 332 * @return If the Set is empty, it means the user has no matching profile.<br> 333 * If the Set is non empty, it contains at least one of the given profile BUT it may not contains all the matching profiles for the user AND it can contains some other profiles that were not in the given profiles 334 */ 335 public Set<String> hasAnyConnectedUserAnyPermission(Set<? extends Object> rootContexts, Set<String> profileIds) 336 { 337 getLogger().debug("Try to determine permissions on any context for any connected user with profiles {}", profileIds); 338 339 List<ProfileAssignmentStorage> sortedPas = getExtensionsIds().stream() 340 .map(this::getExtension) 341 .sorted(Comparator.comparing(ProfileAssignmentStorage::getPriority)) 342 .collect(Collectors.toList()); 343 344 Set<? extends Object> remainingRootsToTest = new HashSet<>(rootContexts); 345 for (ProfileAssignmentStorage profileAssignmentStorage : sortedPas) 346 { 347 if (!remainingRootsToTest.isEmpty()) 348 { 349 Set<? extends Object> filteredContexts = remainingRootsToTest.stream().filter(profileAssignmentStorage::isRootContextSupported).collect(Collectors.toSet()); 350 if (!filteredContexts.isEmpty()) 351 { 352 Set<String> hasAnyConnectedUserAnyPermission = _hasAnyConnectedUserAnyPermission(profileAssignmentStorage, filteredContexts, profileIds); 353 if (!hasAnyConnectedUserAnyPermission.isEmpty()) 354 { 355 getLogger().debug("Find permission on any context for any connected user with profiles {}", profileIds); 356 return hasAnyConnectedUserAnyPermission; 357 } 358 } 359 // Remove the already supported contexts 360 remainingRootsToTest.removeAll(filteredContexts); 361 } 362 } 363 364 getLogger().debug("Find no permission on any context for any connected user with profiles {}", profileIds); 365 return Set.of(); 366 } 367 368 private Set<String> _hasAnyConnectedUserAnyPermission(ProfileAssignmentStorage profileAssignmentStorage, Set< ? extends Object> rootContexts, Set<String> profileIds) 369 { 370 if (profileIds.isEmpty() || rootContexts.isEmpty()) 371 { 372 return Set.of(); 373 } 374 375 376 // 1) Search at least one profile in "allowed-anonymous-profiles", if found return true 377 Set<String> hasAnonymousAnyAllowedProfile = profileAssignmentStorage.hasAnonymousAnyAllowedProfile(rootContexts, profileIds); 378 if (!hasAnonymousAnyAllowedProfile.isEmpty()) 379 { 380 return hasAnonymousAnyAllowedProfile; 381 } 382 383 // 2) Search at least one profile in "allowed-any-connected-profiles", if found return true 384 Set<String> hasAnyConnectedAnyAllowedProfile = profileAssignmentStorage.hasAnyConnectedAnyAllowedProfile(rootContexts, profileIds); 385 if (!hasAnyConnectedAnyAllowedProfile.isEmpty()) 386 { 387 return hasAnyConnectedAnyAllowedProfile; 388 } 389 390 // 3) Not found, return false 391 return Set.of(); 392 } 393 394 /** 395 * Gets the permissions a user has on an object, for every profile in the application. 396 * @param user The user 397 * @param userGroups The groups 398 * @param object The object 399 * @return the permissions a user has on an object, for every profile in the application. 400 */ 401 public Map<String, AccessResult> getPermissionsByProfile(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 402 { 403 getLogger().debug("Try to determine permissions for each profile on context {} for user '{}' and groups {}", object, user, userGroups); 404 405 Map<String, AccessResult> result = new HashMap<>(); 406 407 // Allowed profiles for anonymous 408 Map<AnonymousOrAnyConnectedKeys, Set<String>> profilesForAnonymousAndAnyConnectedUser = getProfilesForAnonymousAndAnyConnectedUser(object); 409 Set<String> anonymousAllowed = Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_ALLOWED)).orElse(Set.of()); 410 Set<String> anonymousDenied = Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED)).orElse(Set.of()); 411 ((Collection<String>) CollectionUtils.removeAll(anonymousAllowed, anonymousDenied)) 412 .forEach(profile -> result.putIfAbsent(profile, AccessResult.ANONYMOUS_ALLOWED)); 413 414 // Denied profiles for user 415 Map<UserIdentity, Map<UserOrGroup, Set<String>>> profilesForUsers = getProfilesForUsers(object, user); 416 Optional.ofNullable(profilesForUsers.get(user)).map(a -> a.get(UserOrGroup.DENIED)).orElse(Set.of()) 417 .forEach(profile -> result.putIfAbsent(profile, AccessResult.USER_DENIED)); 418 419 // Allowed profiles for user 420 Optional.ofNullable(profilesForUsers.get(user)).map(a -> a.get(UserOrGroup.ALLOWED)).orElse(Set.of()) 421 .forEach(profile -> result.putIfAbsent(profile, AccessResult.USER_ALLOWED)); 422 423 // Denied profiles for groups 424 Map<GroupIdentity, Map<UserOrGroup, Set<String>>> profilesForGroups = getProfilesForGroups(object, userGroups); 425 for (GroupIdentity userGroup : userGroups) 426 { 427 Optional.ofNullable(profilesForGroups.get(userGroup)).map(a -> a.get(UserOrGroup.DENIED)).orElse(Set.of()) 428 .forEach(profile -> result.putIfAbsent(profile, AccessResult.GROUP_DENIED)); 429 } 430 431 // Allowed profiles for groups 432 for (GroupIdentity userGroup : userGroups) 433 { 434 Optional.ofNullable(profilesForGroups.get(userGroup)).map(a -> a.get(UserOrGroup.ALLOWED)).orElse(Set.of()) 435 .forEach(profile -> result.putIfAbsent(profile, AccessResult.GROUP_ALLOWED)); 436 } 437 438 // Denied profiles for any connected user 439 Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_DENIED)).orElse(Set.of()) 440 .forEach(profile -> result.putIfAbsent(profile, AccessResult.ANY_CONNECTED_DENIED)); 441 442 // Allowed profiles for any connected user 443 Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_ALLOWED)).orElse(Set.of()) 444 .forEach(profile -> result.putIfAbsent(profile, AccessResult.ANY_CONNECTED_ALLOWED)); 445 446 // Denied profiles for anonymous 447 anonymousDenied 448 .forEach(profile -> result.putIfAbsent(profile, AccessResult.ANONYMOUS_DENIED)); 449 450 getLogger().debug("The permissions by profile on context {} for user '{}' and groups {} are : {}", object, user, userGroups, result); 451 return result; 452 } 453 454 /** 455 * Gets the permissions for Anonymous for the given profiles 456 * @param profileIds The profiles to get permissions on 457 * @param object The object 458 * @return the access result for each profile 459 */ 460 public AccessResult getPermissionForAnonymous (Set<String> profileIds, Object object) 461 { 462 getLogger().debug("Try to determine permission for Anonymous on context {} and profiles {}", object, profileIds); 463 464 Map<AnonymousOrAnyConnectedKeys, Set<String>> profilesForAnonymousAndAnyConnectedUser = getProfilesForAnonymousAndAnyConnectedUser(object); 465 466 Set<String> deniedProfiles = Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED)).orElse(Set.of()); 467 Set<String> allowedProfiles = Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_ALLOWED)).orElse(Set.of()); 468 469 AccessResult result = AccessResult.UNKNOWN; 470 471 for (String profileId : profileIds) 472 { 473 if (deniedProfiles.contains(profileId)) 474 { 475 return AccessResult.ANONYMOUS_DENIED; 476 } 477 else if (allowedProfiles.contains(profileId)) 478 { 479 result = AccessResult.ANONYMOUS_ALLOWED; 480 } 481 } 482 483 return result; 484 } 485 486 /** 487 * Gets the permissions for Anonymous for the given profiles 488 * @param profileIds The profiles to get permissions on 489 * @param object The object 490 * @return the access result for each profile 491 */ 492 public AccessResult getPermissionForAnyConnectedUser (Set<String> profileIds, Object object) 493 { 494 getLogger().debug("Try to determine permission for AnyConnectedUser on context {} and profiles {}", object, profileIds); 495 496 Map<AnonymousOrAnyConnectedKeys, Set<String>> profilesForAnonymousAndAnyConnectedUser = getProfilesForAnonymousAndAnyConnectedUser(object); 497 498 Set<String> deniedProfiles = Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_DENIED)).orElse(Set.of()); 499 Set<String> allowedProfiles = Optional.ofNullable(profilesForAnonymousAndAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_ALLOWED)).orElse(Set.of()); 500 501 AccessResult result = AccessResult.UNKNOWN; 502 503 for (String profileId : profileIds) 504 { 505 if (deniedProfiles.contains(profileId)) 506 { 507 return AccessResult.ANY_CONNECTED_DENIED; 508 } 509 else if (allowedProfiles.contains(profileId)) 510 { 511 result = AccessResult.ANY_CONNECTED_ALLOWED; 512 } 513 } 514 515 return result; 516 } 517 518 private AccessResult _getPermissionsByUser(Map<UserOrGroup, Set<String>> userProfiles, Set<String> profileIds) 519 { 520 if (userProfiles != null) 521 { 522 Set<String> deniedProfiles = userProfiles.get(UserOrGroup.DENIED); 523 if (deniedProfiles != null) 524 { 525 if (deniedProfiles.stream().anyMatch(p -> profileIds.contains(p))) 526 { 527 return AccessResult.USER_DENIED; 528 } 529 } 530 531 Set<String> allowedProfiles = userProfiles.get(UserOrGroup.ALLOWED); 532 if (allowedProfiles != null) 533 { 534 if (allowedProfiles.stream().anyMatch(p -> profileIds.contains(p))) 535 { 536 return AccessResult.USER_ALLOWED; 537 } 538 } 539 } 540 541 return null; 542 } 543 544 /** 545 * Gets the permission by user only on an object, according to the given profiles. It does not take account of the groups of the user, etc. 546 * @param profileIds The ids of the profiles 547 * @param object The object 548 * @return the permission by user only on an object, according to the given profiles 549 */ 550 public Map<UserIdentity, AccessResult> getPermissionsByUser(Set<String> profileIds, Object object) 551 { 552 getLogger().debug("Try to determine permissions by users on context {} and profiles {}", object, profileIds); 553 554 Map<UserIdentity, AccessResult> result = getProfilesForUsers(object, null) 555 .entrySet() 556 .stream() 557 .map(entry -> Pair.of(entry.getKey(), _getPermissionsByUser(entry.getValue(), profileIds))) 558 .filter(pair -> pair.getRight() != null) 559 .collect(Collectors.toMap(Pair::getLeft, Pair::getRight)); 560 561 getLogger().debug("The permissions by users on context {} and profiles {} are: {}", object, profileIds, result); 562 return result; 563 } 564 565 private AccessResult _getPermissionsByGroup(Map<UserOrGroup, Set<String>> groupProfiles, Set<String> profileIds) 566 { 567 if (groupProfiles != null) 568 { 569 Set<String> deniedProfiles = groupProfiles.get(UserOrGroup.DENIED); 570 if (deniedProfiles != null) 571 { 572 if (deniedProfiles.stream().anyMatch(p -> profileIds.contains(p))) 573 { 574 return AccessResult.GROUP_DENIED; 575 } 576 } 577 578 Set<String> allowedProfiles = groupProfiles.get(UserOrGroup.ALLOWED); 579 if (allowedProfiles != null) 580 { 581 if (allowedProfiles.stream().anyMatch(p -> profileIds.contains(p))) 582 { 583 return AccessResult.GROUP_ALLOWED; 584 } 585 } 586 } 587 588 return null; 589 } 590 591 /** 592 * Gets the permission by group only on an object, according to the given profiles. 593 * @param profileIds The ids of the profiles 594 * @param object The object 595 * @return the permission by group only on an object, according to the given profiles 596 */ 597 public Map<GroupIdentity, AccessResult> getPermissionsByGroup(Set<String> profileIds, Object object) 598 { 599 getLogger().debug("Try to determine permissions by groups on context {} and profiles {}", object, profileIds); 600 601 Map<GroupIdentity, AccessResult> result = getProfilesForGroups(object, null) 602 .entrySet() 603 .stream() 604 .map(entry -> Pair.of(entry.getKey(), _getPermissionsByGroup(entry.getValue(), profileIds))) 605 .filter(pair -> pair.getRight() != null) 606 .collect(Collectors.toMap(Pair::getLeft, Pair::getRight)); 607 608 getLogger().debug("The permissions by groups on context {} and profiles {} are: ", object, profileIds, result); 609 return result; 610 } 611 612 /* ------- */ 613 /* STORAGE */ 614 /* ------- */ 615 616 /** 617 * Gets the allowed profiles any connected user has on the given object 618 * @param context The object 619 * @return a map containing allowed/denied profiles that anonymous and any connected user has on the given object 620 */ 621 public Map<AnonymousOrAnyConnectedKeys, Set<String>> getProfilesForAnonymousAndAnyConnectedUser(Object context) 622 { 623 return _getFirstProfileAssignmentStorage(context) 624 .map(pas -> pas.getProfilesForAnonymousAndAnyConnectedUser(context)) 625 .orElse(Map.of()); 626 } 627 628 /** 629 * Gets the users that have allowed profiles assigned on the given object 630 * @param context The object to test 631 * @param user The user to get profiles for. Can be null to get profiles for all users that have rights 632 * @return The map of allowed users with their assigned allowed/denied profiles 633 */ 634 public Map<UserIdentity, Map<UserOrGroup, Set<String>>> getProfilesForUsers(Object context, UserIdentity user) 635 { 636 return _getFirstProfileAssignmentStorage(context) 637 .map(pas -> pas.getProfilesForUsers(context, user)) 638 .orElse(Map.of()); 639 } 640 641 /** 642 * Gets the groups that have allowed profiles assigned on the given object 643 * @param context The object to test 644 * @param groups The group to get profiles for. Can be null to get profiles for all groups that have rights 645 * @return The map of allowed/denied groups with their assigned profiles 646 */ 647 public Map<GroupIdentity, Map<UserOrGroup, Set<String>>> getProfilesForGroups(Object context, Set<GroupIdentity> groups) 648 { 649 return _getFirstProfileAssignmentStorage(context) 650 .map(pas -> pas.getProfilesForGroups(context, groups)) 651 .orElse(Map.of()); 652 } 653 654 /* ------------- */ 655 /* ANY CONNECTED */ 656 /* ------------- */ 657 658 /** 659 * Adds allowed profile any connected user has on the given object 660 * @param context The object context 661 * @param profileId The profile to add 662 */ 663 public void allowProfileToAnyConnectedUser(String profileId, Object context) 664 { 665 _getFirstModifiableProfileAssignmentStorage(context) 666 .ifPresent(pas -> pas.addAllowedProfilesForAnyConnectedUser(context, Collections.singleton(profileId))); 667 } 668 669 /** 670 * Adds denied profile any connected user has on the given object 671 * @param profileId The profile to add 672 * @param context The object context 673 */ 674 public void denyProfileToAnyConnectedUser(String profileId, Object context) 675 { 676 _getFirstModifiableProfileAssignmentStorage(context) 677 .ifPresent(pas -> pas.addDeniedProfilesForAnyConnectedUser(context, Collections.singleton(profileId))); 678 } 679 680 /** 681 * Removes allowed profile any connected user has on the given object 682 * @param profileId The profile to remove 683 * @param context The object context 684 */ 685 public void removeAllowedProfileFromAnyConnectedUser(String profileId, Object context) 686 { 687 _getFirstModifiableProfileAssignmentStorage(context) 688 .ifPresent(pas -> pas.removeAllowedProfilesForAnyConnectedUser(context, Collections.singleton(profileId))); 689 } 690 691 /** 692 * Removes denied profile any connected user has on the given object 693 * @param context The object context 694 * @param profileId The profile to remove 695 */ 696 public void removeDeniedProfileFromAnyConnectedUser(String profileId, Object context) 697 { 698 _getFirstModifiableProfileAssignmentStorage(context) 699 .ifPresent(pas -> pas.removeDeniedProfilesForAnyConnectedUser(context, Collections.singleton(profileId))); 700 } 701 702 /* --------- */ 703 /* ANONYMOUS */ 704 /* --------- */ 705 706 /** 707 * Adds allowed profile an anonymous user has on the given object 708 * @param profileId The profile to add 709 * @param context The object context 710 */ 711 public void allowProfileToAnonymous(String profileId, Object context) 712 { 713 _getFirstModifiableProfileAssignmentStorage(context) 714 .ifPresent(pas -> pas.addAllowedProfilesForAnonymous(context, Collections.singleton(profileId))); 715 } 716 717 /** 718 * Adds denied profile an anonymous user has on the given object 719 * @param profileId The profile to add 720 * @param context The object context 721 */ 722 public void denyProfileToAnonymous(String profileId, Object context) 723 { 724 _getFirstModifiableProfileAssignmentStorage(context) 725 .ifPresent(pas -> pas.addDeniedProfilesForAnonymous(context, Collections.singleton(profileId))); 726 } 727 728 /** 729 * Removes allowed profile an anonymous user has on the given object 730 * @param profileId The profile to remove 731 * @param context The object context 732 */ 733 public void removeAllowedProfileFromAnonymous(String profileId, Object context) 734 { 735 _getFirstModifiableProfileAssignmentStorage(context) 736 .ifPresent(pas -> pas.removeAllowedProfilesForAnonymous(context, Collections.singleton(profileId))); 737 } 738 739 /** 740 * Removes denied profile an anonymous user has on the given object 741 * @param context The object context 742 * @param profileId The profile to remove 743 */ 744 public void removeDeniedProfileFromAnonymous(String profileId, Object context) 745 { 746 _getFirstModifiableProfileAssignmentStorage(context) 747 .ifPresent(pas -> pas.removeDeniedProfilesForAnonymous(context, Collections.singleton(profileId))); 748 } 749 750 /* ----- */ 751 /* USERS */ 752 /* ----- */ 753 754 /** 755 * Allows a user to a profile on a given object 756 * @param user The user to add 757 * @param profileId The id of the profile 758 * @param context The object context 759 */ 760 public void allowProfileToUser(UserIdentity user, String profileId, Object context) 761 { 762 _getFirstModifiableProfileAssignmentStorage(context) 763 .ifPresent(pas -> pas.addAllowedUsers(Collections.singleton(user), context, profileId)); 764 } 765 766 /** 767 * Denies a user to a profile on a given object 768 * @param user The user to add 769 * @param profileId The id of the profile 770 * @param context The object context 771 */ 772 public void denyProfileToUser(UserIdentity user, String profileId, Object context) 773 { 774 _getFirstModifiableProfileAssignmentStorage(context) 775 .ifPresent(pas -> pas.addDeniedUsers(Collections.singleton(user), context, profileId)); 776 } 777 778 /** 779 * Removes the association between a user and an allowed profile on a given object 780 * @param user The user to remove 781 * @param context The object context 782 * @param profileId The id of the profile 783 */ 784 public void removeAllowedProfileFromUser(UserIdentity user, String profileId, Object context) 785 { 786 _getFirstModifiableProfileAssignmentStorage(context) 787 .ifPresent(pas -> pas.removeAllowedUsers(Collections.singleton(user), context, profileId)); 788 } 789 790 /** 791 * Removes the association between a user and a denied profile on a given object 792 * @param user The user to remove 793 * @param profileId The id of the profile 794 * @param context The object context 795 */ 796 public void removeDeniedProfileFromUser(UserIdentity user, String profileId, Object context) 797 { 798 _getFirstModifiableProfileAssignmentStorage(context) 799 .ifPresent(pas -> pas.removeDeniedUsers(Collections.singleton(user), context, profileId)); 800 } 801 802 803 /* ------ */ 804 /* GROUPS */ 805 /* ------ */ 806 807 /** 808 * Allows a group to a profile on a given object 809 * @param group The group to add 810 * @param profileId The id of the profile 811 * @param context The object context 812 */ 813 public void allowProfileToGroup(GroupIdentity group, String profileId, Object context) 814 { 815 _getFirstModifiableProfileAssignmentStorage(context) 816 .ifPresent(pas -> pas.addAllowedGroups(Collections.singleton(group), context, profileId)); 817 } 818 819 /** 820 * Denies a group to a profile on a given object 821 * @param group The group to add 822 * @param profileId The id of the profile 823 * @param context The object context 824 */ 825 public void denyProfileToGroup(GroupIdentity group, String profileId, Object context) 826 { 827 _getFirstModifiableProfileAssignmentStorage(context) 828 .ifPresent(pas -> pas.addDeniedGroups(Collections.singleton(group), context, profileId)); 829 } 830 831 /** 832 * Removes the association between a group and an allowed profile on a given object 833 * @param group The group to remove 834 * @param profileId The id of the profile 835 * @param context The object context 836 */ 837 public void removeAllowedProfileFromGroup(GroupIdentity group, String profileId, Object context) 838 { 839 _getFirstModifiableProfileAssignmentStorage(context) 840 .ifPresent(pas -> pas.removeAllowedGroups(Collections.singleton(group), context, profileId)); 841 } 842 843 /** 844 * Removes the association between a group and a denied profile on a given object 845 * @param group The group to remove 846 * @param profileId The id of the profile 847 * @param context The object context 848 */ 849 public void removeDeniedProfileFromGroup(GroupIdentity group, String profileId, Object context) 850 { 851 _getFirstModifiableProfileAssignmentStorage(context) 852 .ifPresent(pas -> pas.removeDeniedGroups(Collections.singleton(group), context, profileId)); 853 } 854 855 /* ----------- */ 856 /* INHERITANCE */ 857 /* ----------- */ 858 /** 859 * Determines if the inheritance of permissions is disallowed on a given context 860 * @param context The object context 861 * @return true if the inheritance is disallowed 862 */ 863 public boolean isInheritanceDisallowed(Object context) 864 { 865 return _getFirstProfileAssignmentStorage(context) 866 .map(pas -> pas.isInheritanceDisallowed(context)) 867 .orElse(Boolean.FALSE); 868 } 869 870 /** 871 * Allow or disallow the inheritance of permissions on a given context 872 * @param context The object context 873 * @param disallow true to disallow the inheritance 874 */ 875 public void disallowInheritance(Object context, boolean disallow) 876 { 877 _getFirstModifiableProfileAssignmentStorage(context) 878 .ifPresent(pas -> pas.disallowInheritance(context, disallow)); 879 } 880 881 /* -------------------------- */ 882 /* PRIVATE CONVENIENT METHODS */ 883 /* -------------------------- */ 884 885 private Optional<ProfileAssignmentStorage> _getFirstProfileAssignmentStorage(Object object) 886 { 887 return getExtensionsIds().stream() 888 .map(this::getExtension) 889 .sorted(Comparator.comparing(ProfileAssignmentStorage::getPriority)) 890 .filter(pas -> pas.isSupported(object)) 891 .findFirst(); 892 } 893 894 private Optional<ModifiableProfileAssignmentStorage> _getFirstModifiableProfileAssignmentStorage(Object object) 895 { 896 return getExtensionsIds().stream() 897 .map(this::getExtension) 898 .sorted(Comparator.comparing(ProfileAssignmentStorage::getPriority)) 899 .filter(pas -> pas.isSupported(object) && pas instanceof ModifiableProfileAssignmentStorage) 900 .map(ModifiableProfileAssignmentStorage.class::cast) 901 .findFirst(); 902 } 903}