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.io.InputStream; 019import java.util.Collections; 020import java.util.Enumeration; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Map.Entry; 026import java.util.Optional; 027import java.util.Set; 028import java.util.stream.Collectors; 029 030import org.apache.avalon.framework.activity.Initializable; 031import org.apache.avalon.framework.component.Component; 032import org.apache.avalon.framework.configuration.Configurable; 033import org.apache.avalon.framework.configuration.Configuration; 034import org.apache.avalon.framework.configuration.ConfigurationException; 035import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 036import org.apache.avalon.framework.context.Context; 037import org.apache.avalon.framework.context.ContextException; 038import org.apache.avalon.framework.context.Contextualizable; 039import org.apache.avalon.framework.service.ServiceException; 040import org.apache.avalon.framework.service.ServiceManager; 041import org.apache.avalon.framework.service.Serviceable; 042import org.apache.avalon.framework.thread.ThreadSafe; 043import org.apache.cocoon.components.ContextHelper; 044import org.apache.cocoon.environment.Request; 045import org.apache.commons.lang3.StringUtils; 046import org.apache.excalibur.source.Source; 047import org.apache.excalibur.source.SourceResolver; 048 049import org.ametys.core.cache.AbstractCacheManager; 050import org.ametys.core.cache.Cache; 051import org.ametys.core.group.GroupDirectoryDAO; 052import org.ametys.core.group.GroupIdentity; 053import org.ametys.core.group.GroupManager; 054import org.ametys.core.right.AccessController.AccessResult; 055import org.ametys.core.user.CurrentUserProvider; 056import org.ametys.core.user.UserIdentity; 057import org.ametys.core.user.UserManager; 058import org.ametys.core.user.population.PopulationContextHelper; 059import org.ametys.core.user.population.UserPopulationDAO; 060import org.ametys.plugins.core.impl.cache.AbstractCacheKey; 061import org.ametys.runtime.i18n.I18nizableText; 062import org.ametys.runtime.plugin.component.AbstractLogEnabled; 063 064/** 065 * Abstraction for testing a right associated with a resource and a user from a single source. 066 */ 067public class RightManager extends AbstractLogEnabled implements Serviceable, Configurable, ThreadSafe, Component, Contextualizable, Initializable 068{ 069 /** For avalon service manager */ 070 public static final String ROLE = RightManager.class.getName(); 071 /** The id of the READER profile */ 072 public static final String READER_PROFILE_ID = "READER"; 073 074 /** The instance of ObjectUserIdentity for anonymous */ 075 protected static final UserIdentity __ANONYMOUS_USER_IDENTITY = new UserIdentity(null, null); 076 /** The instance of ObjectUserIdentity for any connected user */ 077 protected static final UserIdentity __ANY_CONNECTED_USER_IDENTITY = new UserIdentity("", ""); 078 079 private static final String CACHE_1 = RightManager.class.getName() + "$Cache-1"; 080 private static final String CACHE_2 = RightManager.class.getName() + "$Cache-2"; 081 082 /** Avalon ServiceManager */ 083 protected ServiceManager _manager; 084 /** Avalon SourceResolver */ 085 protected SourceResolver _resolver; 086 /** The rights' list container */ 087 protected RightsExtensionPoint _rightsEP; 088 /** The extension point for the Right Context Convertors */ 089 protected RightContextConvertorExtensionPoint _rightContextConvertorEP; 090 /** The extension point for Access Controllers */ 091 protected AccessControllerExtensionPoint _accessControllerEP; 092 /** The user manager */ 093 protected UserManager _userManager; 094 /** The group manager */ 095 protected GroupManager _groupManager; 096 /** The DAO for user populations */ 097 protected UserPopulationDAO _userPopulationDAO; 098 /** The DAO for group directories */ 099 protected GroupDirectoryDAO _groupDirectoryDAO; 100 /** The current user provider */ 101 protected CurrentUserProvider _currentUserProvider; 102 /** The rights DAO */ 103 protected RightProfilesDAO _profilesDAO; 104 /** Cache Manager */ 105 protected AbstractCacheManager _cacheManager; 106 107 private Context _context; 108 109 /** 110 * Enumeration of all possible values returned by hasRight(user, right, context) 111 */ 112 public enum RightResult 113 { 114 /** 115 * Indicates that a given user has the required right. 116 */ 117 RIGHT_ALLOW, 118 119 /** 120 * Indicates that a given user does NOT have the required right. 121 */ 122 RIGHT_DENY, 123 124 /** 125 * Indicates that the system knows nothing about the fact that a given user has a right or not. 126 */ 127 RIGHT_UNKNOWN; 128 } 129 130 @Override 131 public void contextualize(Context context) throws ContextException 132 { 133 _context = context; 134 } 135 136 @Override 137 public void service(ServiceManager manager) throws ServiceException 138 { 139 _manager = manager; 140 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 141 _groupManager = (GroupManager) manager.lookup(GroupManager.ROLE); 142 _userPopulationDAO = (UserPopulationDAO) manager.lookup(UserPopulationDAO.ROLE); 143 _groupDirectoryDAO = (GroupDirectoryDAO) manager.lookup(GroupDirectoryDAO.ROLE); 144 _rightsEP = (RightsExtensionPoint) manager.lookup(RightsExtensionPoint.ROLE); 145 _rightContextConvertorEP = (RightContextConvertorExtensionPoint) manager.lookup(RightContextConvertorExtensionPoint.ROLE); 146 _accessControllerEP = (AccessControllerExtensionPoint) manager.lookup(AccessControllerExtensionPoint.ROLE); 147 _resolver = (SourceResolver) _manager.lookup(SourceResolver.ROLE); 148 _currentUserProvider = (CurrentUserProvider) _manager.lookup(CurrentUserProvider.ROLE); 149 _cacheManager = (AbstractCacheManager) manager.lookup(AbstractCacheManager.ROLE); 150 } 151 152 public void initialize() throws Exception 153 { 154 _cacheManager.createRequestCache(CACHE_1, 155 new I18nizableText("plugin.core", "PLUGINS_CORE_RIGHT_MANAGER_CACHE_1_LABEL"), 156 new I18nizableText("plugin.core", "PLUGINS_CORE_RIGHT_MANAGER_CACHE_1_DESCRIPTION"), 157 true); 158 _cacheManager.createRequestCache(CACHE_2, 159 new I18nizableText("plugin.core", "PLUGINS_CORE_RIGHT_MANAGER_CACHE_2_LABEL"), 160 new I18nizableText("plugin.core", "PLUGINS_CORE_RIGHT_MANAGER_CACHE_2_DESCRIPTION"), 161 true); 162 } 163 164 /** 165 * Returns the DAO for profiles 166 * @return The DAO 167 */ 168 protected RightProfilesDAO _getProfileDAO () 169 { 170 try 171 { 172 if (_profilesDAO == null) 173 { 174 _profilesDAO = (RightProfilesDAO) _manager.lookup(RightProfilesDAO.ROLE); 175 } 176 return _profilesDAO; 177 } 178 catch (ServiceException e) 179 { 180 throw new RuntimeException("Failed to retrieve the DAO for profiles", e); 181 } 182 } 183 184 @Override 185 public void configure(Configuration configuration) throws ConfigurationException 186 { 187 Configuration rightsConfiguration = configuration.getChild("rights"); 188 189 String externalFile = rightsConfiguration.getAttribute("config", null); 190 if (externalFile != null) 191 { 192 Source source = null; 193 try 194 { 195 source = _resolver.resolveURI("context://" + externalFile); 196 197 if (source.exists()) 198 { 199 Configuration externalConfiguration; 200 try (InputStream is = source.getInputStream();) 201 { 202 externalConfiguration = new DefaultConfigurationBuilder().build(is); 203 } 204 205 configureRights(externalConfiguration); 206 } 207 else if (getLogger().isInfoEnabled()) 208 { 209 getLogger().info("The optional external rights file '" + externalFile + "' is missing."); 210 } 211 } 212 catch (Exception e) 213 { 214 String message = "An error occured while retriving external file '" + externalFile + "'"; 215 getLogger().error(message, e); 216 throw new ConfigurationException(message, configuration, e); 217 } 218 finally 219 { 220 if (source != null) 221 { 222 _resolver.release(source); 223 } 224 } 225 } 226 else 227 { 228 configureRights(rightsConfiguration); 229 } 230 } 231 232 private void configureRights(Configuration configuration) throws ConfigurationException 233 { 234 Configuration[] rights = configuration.getChildren("right"); 235 for (Configuration rightConf : rights) 236 { 237 String id = rightConf.getAttribute("id", ""); 238 239 String label = rightConf.getChild("label").getValue(""); 240 I18nizableText i18nLabel = new I18nizableText("application", label); 241 242 String description = rightConf.getChild("description").getValue(""); 243 I18nizableText i18nDescription = new I18nizableText("application", description); 244 245 String category = rightConf.getChild("category").getValue(""); 246 I18nizableText i18nCategory = new I18nizableText("application", category); 247 248 if (id.length() == 0 || label.length() == 0 || description.length() == 0 || category.length() == 0) 249 { 250 String message = "Error in " + RightManager.class.getName() + " configuration: attribute 'id' and elements 'label', 'description' and 'category' are mandatory."; 251 getLogger().error(message); 252 throw new ConfigurationException(message, configuration); 253 } 254 255 _rightsEP.addRight(id, i18nLabel, i18nDescription, i18nCategory); 256 } 257 } 258 259 /* --------- */ 260 /* HAS RIGHT */ 261 /* --------- */ 262 263 /** 264 * Checks a permission for the current logged user, on a given object (or context).<br> 265 * If null, it checks if there is at least one object with this permission 266 * @param rightId The name of the right to check. Cannot be null. 267 * @param object The object to check the right. Can be null to search on any object. 268 * @return {@link RightResult#RIGHT_ALLOW}, {@link RightResult#RIGHT_DENY} or {@link RightResult#RIGHT_UNKNOWN} 269 * @throws RightsException if an error occurs. 270 */ 271 public RightResult currentUserHasRight(String rightId, Object object) throws RightsException 272 { 273 return hasRight(_currentUserProvider.getUser(), rightId, object); 274 } 275 276 /** 277 * Checks a permission for a user, on a given object (or context).<br> 278 * If null, it checks if there is at least one object with this permission 279 * @param userIdentity The user identity. Can be null for anonymous 280 * @param rightId The name of the right to check. Cannot be null. 281 * @param object The object to check the right. Can be null to search on any object. 282 * @return {@link RightResult#RIGHT_ALLOW}, {@link RightResult#RIGHT_DENY} or {@link RightResult#RIGHT_UNKNOWN} 283 * @throws RightsException if an error occurs. 284 */ 285 public RightResult hasRight(UserIdentity userIdentity, String rightId, Object object) throws RightsException 286 { 287 UserIdentity objectUserIdentity = userIdentity == null ? __ANONYMOUS_USER_IDENTITY : userIdentity; 288 289 return _hasRight(objectUserIdentity, rightId, object); 290 } 291 292 /** 293 * Gets the right result for anonymous with given right on given object context 294 * @param rightId The id of the right 295 * @param object The object to check 296 * @return the right result for anonymous with given profile on given object context 297 */ 298 public RightResult hasAnonymousRight(String rightId, Object object) 299 { 300 return _hasRight(__ANONYMOUS_USER_IDENTITY, rightId, object); 301 } 302 303 /** 304 * Gets the right result for any connected user with given profile on given object context 305 * @param rightId The right id to test 306 * @param object The object to check 307 * @return the right result for any connected user with given profile on given object context 308 */ 309 public RightResult hasAnyConnectedUserRight(String rightId, Object object) 310 { 311 return _hasRight(__ANY_CONNECTED_USER_IDENTITY, rightId, object); 312 } 313 314 private RightResult _hasRight(UserIdentity userIdentity, String rightId, Object object) 315 { 316 getLogger().debug("Try to determine if user {} has the right '{}' on the object context {}", userIdentity, rightId, object); 317 318 if (StringUtils.isBlank(rightId)) 319 { 320 throw new RightsException("The rightId cannot be null"); 321 } 322 323 return _hasRightOrRead(userIdentity, rightId, object); 324 } 325 326 private RightResult _hasRightOrRead(UserIdentity userIdentity, String rightId, Object object) 327 { 328 if (object == null) 329 { 330 return _hasRightOrRead(userIdentity, rightId); 331 } 332 333 // Try to retrieve in first cache (the one which manages non-null contexts) 334 RightResult cacheResult = _hasRightResultInFirstCache(userIdentity, rightId, object); 335 if (cacheResult != null) 336 { 337 return cacheResult; 338 } 339 340 // Retrieve groups the user belongs to 341 Set<GroupIdentity> groups = _getGroups(userIdentity); 342 343 // Get the objects to check 344 Set<Object> objects = _getConvertedObjects(object, new HashSet<>()); 345 346 // Retrieve the set of AccessResult 347 Set<AccessResult> accessResults = _getAccessResults(userIdentity, groups, rightId, objects); 348 349 // Compute access 350 AccessResult access = AccessResult.merge(accessResults); 351 352 RightResult rightResult = access.toRightResult(); 353 _putInFirstCache(userIdentity, rightId, object, rightResult); 354 355 return rightResult; 356 } 357 358 /** 359 * Has the user/anonymous/anyconnected the non null right on any content of the current workspace? 360 * @param userIdentity The user connecter or the value for anonymous or any connected user 361 * @param rightId The right id to test. Can be null to test read access 362 * @return The computed right result 363 */ 364 private RightResult _hasRightOrRead(UserIdentity userIdentity, String rightId) 365 { 366 // Resolve contexts 367 Set<Object> workspacesContexts = _getConvertedObjects("/${WorkspaceName}", new HashSet<>()); 368 369 // Try to retrieve in second cache (the one which manages null context) 370 RightResult cacheResult = _hasRightResultInSecondCache(workspacesContexts, userIdentity, rightId); 371 if (cacheResult != null) 372 { 373 return cacheResult; 374 } 375 376 // Retrieve groups the user belongs to 377 Set<GroupIdentity> groups = _getGroups(userIdentity); 378 379 RightResult rightResult = RightResult.RIGHT_UNKNOWN; 380 for (String controllerId : _accessControllerEP.getExtensionsIds()) 381 { 382 AccessController accessController = _accessControllerEP.getExtension(controllerId); 383 try 384 { 385 if (userIdentity == __ANONYMOUS_USER_IDENTITY) 386 { 387 if (rightId == null ? accessController.hasAnonymousAnyReadAccessPermissionOnWorkspace(workspacesContexts) : accessController.hasAnonymousAnyPermissionOnWorkspace(workspacesContexts, rightId)) 388 { 389 rightResult = RightResult.RIGHT_ALLOW; 390 break; 391 } 392 } 393 else if (userIdentity == __ANY_CONNECTED_USER_IDENTITY) 394 { 395 if (rightId == null ? accessController.hasAnyConnectedUserAnyReadAccessPermissionOnWorkspace(workspacesContexts) : accessController.hasAnyConnectedUserAnyPermissionOnWorkspace(workspacesContexts, rightId)) 396 { 397 rightResult = RightResult.RIGHT_ALLOW; 398 break; 399 } 400 } 401 else 402 { 403 if (rightId == null ? accessController.hasUserAnyReadAccessPermissionOnWorkspace(workspacesContexts, userIdentity, groups) : accessController.hasUserAnyPermissionOnWorkspace(workspacesContexts, userIdentity, groups, rightId)) 404 { 405 rightResult = RightResult.RIGHT_ALLOW; 406 break; 407 } 408 } 409 } 410 catch (Exception e) 411 { 412 getLogger().error("An error occured with controller '{}'. Thus, this controller will be ignored.", controllerId, e); 413 } 414 } 415 416 getLogger().debug("Right result found for [{}, {}, {}] => {}", workspacesContexts, userIdentity, rightId, rightResult); 417 _putInSecondCache(workspacesContexts, userIdentity, rightId, rightResult); 418 return rightResult; 419 } 420 421 private Set<AccessResult> _getAccessResults(UserIdentity userIdentity, Set<GroupIdentity> groups, String rightId, Set<Object> objects) 422 { 423 Set<AccessResult> accessResults = new HashSet<>(); 424 for (Object obj : objects) 425 { 426 for (String controllerId : _accessControllerEP.getExtensionsIds()) 427 { 428 AccessController accessController = _accessControllerEP.getExtension(controllerId); 429 try 430 { 431 if (accessController.isSupported(obj)) 432 { 433 if (userIdentity == __ANONYMOUS_USER_IDENTITY) 434 { 435 accessResults.add(rightId == null ? accessController.getReadAccessPermissionForAnonymous(obj) : accessController.getPermissionForAnonymous(rightId, obj)); 436 } 437 else if (userIdentity == __ANY_CONNECTED_USER_IDENTITY) 438 { 439 accessResults.add(rightId == null ? accessController.getReadAccessPermissionForAnyConnectedUser(obj) : accessController.getPermissionForAnyConnectedUser(rightId, obj)); 440 } 441 else 442 { 443 accessResults.add(rightId == null ? accessController.getReadAccessPermission(userIdentity, groups, obj) : accessController.getPermission(userIdentity, groups, rightId, obj)); 444 } 445 } 446 else 447 { 448 accessResults.add(AccessResult.UNKNOWN); 449 } 450 } 451 catch (Exception e) 452 { 453 getLogger().error("An error occured with controller '{}' for object {}. Thus, this controller will be ignored.", controllerId, obj, e); 454 } 455 } 456 } 457 458 return accessResults; 459 } 460 461 /* --------------- */ 462 /* HAS READ ACCESS */ 463 /* --------------- */ 464 465 /** 466 * Returns true if the current user has READ access on the given object 467 * @param object The object to check the right. Can be null to search on any object. 468 * @return true if the given user has READ access on the given object 469 */ 470 public boolean currentUserHasReadAccess(Object object) 471 { 472 return hasReadAccess(_currentUserProvider.getUser(), object); 473 } 474 475 /** 476 * Returns true if the given user has READ access on the given object 477 * @param userIdentity The user identity. Cannot be null. 478 * @param object The object to check the right. Can be null to search on any object. 479 * @return true if the given user has READ access on the given object 480 */ 481 public boolean hasReadAccess(UserIdentity userIdentity, Object object) 482 { 483 UserIdentity objectUserIdentity = userIdentity == null ? __ANONYMOUS_USER_IDENTITY : userIdentity; 484 485 return _hasRightOrRead(objectUserIdentity, null, object) == RightResult.RIGHT_ALLOW; 486 } 487 488 /** 489 * Returns true if the object is not restricted, i.e. an anonymous user has READ access (is allowed) on the object 490 * @param object The object to check. Cannot be null 491 * @return true if the object is restricted, i.e. an anonymous user has READ access (is allowed) on the object 492 */ 493 public boolean hasAnonymousReadAccess(Object object) 494 { 495 return _hasRightOrRead(__ANONYMOUS_USER_IDENTITY, null, object) == RightResult.RIGHT_ALLOW; 496 } 497 498 /** 499 * Returns true if any connected user has READ access allowed on the object 500 * @param object The object to check. Cannot be null 501 * @return true if any connected user has READ access allowed on the object 502 */ 503 public boolean hasAnyConnectedUserReadAccess(Object object) 504 { 505 return _hasRightOrRead(__ANY_CONNECTED_USER_IDENTITY, null, object) == RightResult.RIGHT_ALLOW; 506 } 507 508 /* ------------- */ 509 /* ALLOWED USERS */ 510 /* ------------- */ 511 512 /** 513 * Get the list of users that have a particular right in a particular context. 514 * @param rightId The name of the right to check. Cannot be null. 515 * @param object The object to check the right. Cannot be null. 516 * @return The list of users allowed with that right as a Set of String (user identities). 517 * @throws RightsException if an error occurs. 518 */ 519 public AllowedUsers getAllowedUsers(String rightId, Object object) 520 { 521 if (StringUtils.isBlank(rightId)) 522 { 523 throw new RightsException("The rightId cannot be null"); 524 } 525 526 return _getAllowedUsers(rightId, object); 527 } 528 529 /** 530 * Get the users with a READ access on given object 531 * @param object The object 532 * @return The representation of allowed users 533 */ 534 public AllowedUsers getReadAccessAllowedUsers(Object object) 535 { 536 return _getAllowedUsers(null, object); 537 } 538 539 private AllowedUsers _getAllowedUsers(String rightId, Object object) 540 { 541 Optional.ofNullable(object).orElseThrow(() -> 542 { 543 return new RightsException("The object cannot be null"); 544 }); 545 546 // Get the objects to check 547 Set<Object> objects = _getConvertedObjects(object, new HashSet<>()); 548 549 // For each object, retrieve the allowed and denied users/groups 550 Boolean isAnyConnectedAllowed = null; // unknown 551 Set<UserIdentity> allAllowedUsers = new HashSet<>(); 552 Set<UserIdentity> allDeniedUsers = new HashSet<>(); 553 Set<GroupIdentity> allAllowedGroups = new HashSet<>(); 554 Set<GroupIdentity> allDeniedGroups = new HashSet<>(); 555 556 for (Object obj : objects) 557 { 558 for (String controllerId : _accessControllerEP.getExtensionsIds()) 559 { 560 AccessController accessController = _accessControllerEP.getExtension(controllerId); 561 try 562 { 563 if (accessController.isSupported(obj)) 564 { 565 if ((rightId == null ? accessController.getReadAccessPermissionForAnonymous(obj) : accessController.getPermissionForAnonymous(rightId, obj)) == AccessResult.ANONYMOUS_ALLOWED) 566 { 567 // Any anonymous user is allowed 568 return new AllowedUsers(true, false, null, null, null, null, _userManager, _groupManager, null); 569 } 570 571 AccessResult permissionForAnyConnectedUser = rightId == null ? accessController.getReadAccessPermissionForAnyConnectedUser(obj) : accessController.getPermissionForAnyConnectedUser(rightId, obj); 572 if (permissionForAnyConnectedUser == AccessResult.ANY_CONNECTED_DENIED) 573 { 574 // For having any connected user allowed, you need to not have the denied access for one object 575 isAnyConnectedAllowed = Boolean.FALSE; 576 } 577 else if (isAnyConnectedAllowed == null && permissionForAnyConnectedUser == AccessResult.ANY_CONNECTED_ALLOWED) 578 { 579 isAnyConnectedAllowed = Boolean.TRUE; 580 } 581 582 Map<UserIdentity, AccessResult> permissionsByUser = rightId == null ? accessController.getReadAccessPermissionByUser(obj) : accessController.getPermissionByUser(rightId, obj); 583 584 Set<UserIdentity> allowedUsersOnObj = permissionsByUser.entrySet().stream() 585 .filter(entry -> AccessResult.USER_ALLOWED.equals(entry.getValue())) 586 .map(Entry::getKey) 587 .collect(Collectors.toSet()); 588 allAllowedUsers.addAll(allowedUsersOnObj); 589 590 Set<UserIdentity> deniedUsersOnObj = permissionsByUser.entrySet().stream() 591 .filter(entry -> AccessResult.USER_DENIED.equals(entry.getValue())) 592 .map(Entry::getKey) 593 .collect(Collectors.toSet()); 594 allDeniedUsers.addAll(deniedUsersOnObj); 595 596 597 Map<GroupIdentity, AccessResult> permissionsByGroup = rightId == null ? accessController.getReadAccessPermissionByGroup(obj) : accessController.getPermissionByGroup(rightId, obj); 598 599 Set<GroupIdentity> allowedGroupsOnObj = permissionsByGroup.entrySet().stream() 600 .filter(entry -> AccessResult.GROUP_ALLOWED.equals(entry.getValue())) 601 .map(Entry::getKey) 602 .collect(Collectors.toSet()); 603 allAllowedGroups.addAll(allowedGroupsOnObj); 604 605 Set<GroupIdentity> deniedGroupsOnObj = permissionsByGroup.entrySet().stream() 606 .filter(entry -> AccessResult.GROUP_DENIED.equals(entry.getValue())) 607 .map(Entry::getKey) 608 .collect(Collectors.toSet()); 609 allDeniedGroups.addAll(deniedGroupsOnObj); 610 } 611 } 612 catch (Exception e) 613 { 614 getLogger().error("An error occured with controller '{}' for object {}. Thus, this controller will be ignored.", controllerId, obj, e); 615 } 616 } 617 } 618 619 Request request = ContextHelper.getRequest(_context); 620 @SuppressWarnings("unchecked") 621 List<String> populationContexts = (List<String>) request.getAttribute(PopulationContextHelper.POPULATION_CONTEXTS_REQUEST_ATTR); 622 623 // Then, return the AllowedUsers object 624 return new AllowedUsers(false, isAnyConnectedAllowed != null && isAnyConnectedAllowed.booleanValue(), allAllowedUsers, allDeniedUsers, allAllowedGroups, allDeniedGroups, _userManager, _groupManager, populationContexts != null ? new HashSet<>(populationContexts) : new HashSet<>()); 625 } 626 627 /* --------------- */ 628 /* GET USER RIGHTS */ 629 /* --------------- */ 630 631 /** 632 * Get the list of rights a user is allowed, on a particular object. 633 * @param userIdentity the user identity. Cannot be null. 634 * @param object The object to check the right. Cannot be null. 635 * @return The list of rights as a Set of String (id). 636 * @throws RightsException if an error occurs. 637 */ 638 public Set<String> getUserRights(UserIdentity userIdentity, Object object) throws RightsException 639 { 640 if (userIdentity == null) 641 { 642 throw new RightsException("The userIdentity cannot be null"); 643 } 644 else if (object == null) 645 { 646 throw new RightsException("The object cannot be null"); 647 } 648 649 // Get the objects to check 650 Set<Object> objects = _getConvertedObjects(object, new HashSet<>()); 651 652 // Retrieve groups the user belongs to 653 Set<GroupIdentity> groups = _groupManager.getUserGroups(userIdentity); 654 655 // Gets the access by rights 656 Map<String, AccessResult> accessResultsByRight = _getAccessResultByRight(userIdentity, groups, objects); 657 658 // Keep only positive rights 659 Set<String> allowedRights = accessResultsByRight.entrySet().stream().filter(entry -> entry.getValue().toRightResult() == RightResult.RIGHT_ALLOW).map(entry -> entry.getKey()).collect(Collectors.toSet()); 660 return allowedRights; 661 } 662 663 /** 664 * clear all caches related to RightManager and AccesControllers 665 */ 666 public void clearCaches() 667 { 668 Request request = ContextHelper.getRequest(_context); 669 670 Enumeration<String> attrNames = request.getAttributeNames(); 671 while (attrNames.hasMoreElements()) 672 { 673 String attrName = attrNames.nextElement(); 674 if (attrName != null && attrName.startsWith(AbstractCacheManager.ROLE + "$" + RightManager.ROLE + "$")) 675 { 676 Cache cache = (Cache) request.getAttribute(attrName); 677 if (cache != null) 678 { 679 cache.invalidateAll(); 680 } 681 } 682 } 683 } 684 685 private Map<String, AccessResult> _getAccessResultByRight(UserIdentity userIdentity, Set<GroupIdentity> groups, Set<Object> objects) 686 { 687 Map<String, AccessResult> result = new HashMap<>(); 688 689 for (Object obj : objects) 690 { 691 for (String controllerId : _accessControllerEP.getExtensionsIds()) 692 { 693 AccessController accessController = _accessControllerEP.getExtension(controllerId); 694 try 695 { 696 if (accessController.isSupported(obj)) 697 { 698 // Update the result map 699 Map<String, AccessResult> permissionsByRight = accessController.getPermissionByRight(userIdentity, groups, obj); 700 for (String rightId : permissionsByRight.keySet()) 701 { 702 result.put(rightId, AccessResult.merge(result.get(rightId), permissionsByRight.get(rightId))); 703 } 704 } 705 } 706 catch (Exception e) 707 { 708 getLogger().error("An error occured with controller '{}' for object {}. Thus, this controller will be ignored.", controllerId, obj, e); 709 } 710 } 711 } 712 713 return result; 714 } 715 716 /* ------- */ 717 /* PRIVATE */ 718 /* ------- */ 719 720 private Set<Object> _getConvertedObjects(Object object, Set<Object> alreadyHandledObjects) 721 { 722 Set<Object> finalObjects = new HashSet<>(); 723 724 if (!alreadyHandledObjects.contains(object)) 725 { 726 alreadyHandledObjects.add(object); 727 728 Set<Object> objects = _rightContextConvertorEP.getExtensionsIds().stream() 729 .map(_rightContextConvertorEP::getExtension) 730 .flatMap(convertor -> convertor.convert(object).stream()) 731 .collect(Collectors.toSet()); 732 733 finalObjects.addAll(objects); 734 finalObjects.add(object); 735 736 for (Object convertedObject : objects) 737 { 738 finalObjects.addAll(_getConvertedObjects(convertedObject, alreadyHandledObjects)); 739 } 740 } 741 742 return finalObjects; 743 } 744 745 private Set<GroupIdentity> _getGroups(UserIdentity userIdentity) 746 { 747 if (userIdentity == __ANONYMOUS_USER_IDENTITY || userIdentity == __ANY_CONNECTED_USER_IDENTITY) 748 { 749 return Collections.EMPTY_SET; 750 } 751 else 752 { 753 Set<GroupIdentity> userGroups = _groupManager.getUserGroups(userIdentity); 754 return userGroups; 755 } 756 } 757 758 759 760 private RightResult _hasRightResultInFirstCache(UserIdentity userIdentity, String rightId, Object object) 761 { 762 763 Cache<Cache1Key, RightResult> mapCache = _cacheManager.get(CACHE_1); 764 Cache1Key key = Cache1Key.of(userIdentity, rightId, object); 765 if (mapCache.hasKey(key)) 766 { 767 RightResult cacheResult = mapCache.get(key); 768 getLogger().debug("Find entry in cache for [{}, {}, {}] => {}", userIdentity, rightId, object, cacheResult); 769 return cacheResult; 770 } 771 getLogger().debug("Did not find entry in cache for [{}, {}, {}]", userIdentity, rightId, object); 772 return null; 773 } 774 775 private void _putInFirstCache(UserIdentity userIdentity, String rightId, Object object, RightResult rightResult) 776 { 777 Cache<Cache1Key, RightResult> mapCache = _cacheManager.get(CACHE_1); 778 if (mapCache != null) 779 { 780 mapCache.put(Cache1Key.of(userIdentity, rightId, object), rightResult); 781 } 782 } 783 784 private RightResult _hasRightResultInSecondCache(Set<Object> workspacesContexts, UserIdentity userIdentity, String rightId) 785 { 786 Cache<Cache2Key, RightResult> mapCache = _cacheManager.get(CACHE_2); 787 Cache2Key key = Cache2Key.of(userIdentity, rightId, workspacesContexts); 788 if (mapCache != null) 789 { 790 if (mapCache.hasKey(key)) 791 { 792 RightResult cacheResult = mapCache.get(key); 793 getLogger().debug("Find entry in cache2 for [{}, {}, {}] => {}", workspacesContexts, userIdentity, rightId, cacheResult); 794 return cacheResult; 795 } 796 } 797 798 getLogger().debug("Did not find entry in cache2 for [{}, {}, {}]", workspacesContexts, userIdentity, rightId); 799 return null; 800 } 801 802 private void _putInSecondCache(Set<Object> workspacesContexts, UserIdentity userIdentity, String rightId, RightResult rightResult) 803 { 804 Cache<Cache2Key, RightResult> mapCache = _cacheManager.get(CACHE_2); 805 806 if (mapCache != null) 807 { 808 mapCache.put(Cache2Key.of(userIdentity, rightId, workspacesContexts), rightResult); 809 } 810 811 } 812 813 static class Cache1Key extends AbstractCacheKey 814 { 815 Cache1Key(UserIdentity userIdentity, String rightId, Object object) 816 { 817 super(userIdentity, rightId, object); 818 } 819 820 static Cache1Key of(UserIdentity userIdentity, String rightId, Object object) 821 { 822 return new Cache1Key(userIdentity, StringUtils.defaultString(rightId), object); 823 } 824 825 } 826 static class Cache2Key extends AbstractCacheKey 827 { 828 Cache2Key(UserIdentity userIdentity, String rightId, Set<Object> workspacesContexts) 829 { 830 super(userIdentity, rightId, workspacesContexts); 831 } 832 833 static Cache2Key of(UserIdentity userIdentity, String rightId, Set<Object> workspacesContexts) 834 { 835 return new Cache2Key(userIdentity, StringUtils.defaultString(rightId), workspacesContexts); 836 } 837 838 } 839}