001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.realm; 020 021import org.apache.shiro.authc.credential.CredentialsMatcher; 022import org.apache.shiro.authz.AuthorizationException; 023import org.apache.shiro.authz.AuthorizationInfo; 024import org.apache.shiro.authz.Authorizer; 025import org.apache.shiro.authz.Permission; 026import org.apache.shiro.authz.UnauthorizedException; 027import org.apache.shiro.authz.permission.PermissionResolver; 028import org.apache.shiro.authz.permission.PermissionResolverAware; 029import org.apache.shiro.authz.permission.RolePermissionResolver; 030import org.apache.shiro.authz.permission.RolePermissionResolverAware; 031import org.apache.shiro.authz.permission.WildcardPermissionResolver; 032import org.apache.shiro.cache.Cache; 033import org.apache.shiro.cache.CacheManager; 034import org.apache.shiro.subject.PrincipalCollection; 035import org.apache.shiro.util.CollectionUtils; 036import org.apache.shiro.lang.util.Initializable; 037import org.apache.shiro.lang.util.StringUtils; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import java.util.ArrayList; 042import java.util.Arrays; 043import java.util.Collection; 044import java.util.Collections; 045import java.util.HashSet; 046import java.util.LinkedHashSet; 047import java.util.List; 048import java.util.Set; 049import java.util.concurrent.atomic.AtomicInteger; 050 051/** 052 * An {@code AuthorizingRealm} extends the {@code AuthenticatingRealm}'s capabilities by adding Authorization 053 * (access control) support. 054 * <p/> 055 * This implementation will perform all role and permission checks automatically (and subclasses do not have to 056 * write this logic) as long as the 057 * {@link #getAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} method returns an 058 * {@link AuthorizationInfo}. Please see that method's JavaDoc for an in-depth explanation. 059 * <p/> 060 * If you find that you do not want to utilize the {@link AuthorizationInfo AuthorizationInfo} construct, 061 * you are of course free to subclass the {@link AuthenticatingRealm AuthenticatingRealm} directly instead and 062 * implement the remaining Realm interface methods directly. You might do this if you want to have better control 063 * over how the Role and Permission checks occur for your specific data source. However, using AuthorizationInfo 064 * (and its default implementation {@link org.apache.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}) 065 * is sufficient in the large 066 * majority of Realm cases. 067 * 068 * @see org.apache.shiro.authz.SimpleAuthorizationInfo 069 * @since 0.2 070 */ 071@SuppressWarnings({"checkstyle:MethodCount"}) 072public abstract class AuthorizingRealm extends AuthenticatingRealm 073 implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware { 074 075 /*------------------------------------------- 076 | C O N S T A N T S | 077 ============================================*/ 078 private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizingRealm.class); 079 080 /** 081 * The default suffix appended to the realm name for caching AuthorizationInfo instances. 082 */ 083 private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authorizationCache"; 084 085 private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(); 086 087 /*------------------------------------------- 088 | I N S T A N C E V A R I A B L E S | 089 ============================================*/ 090 /** 091 * The cache used by this realm to store AuthorizationInfo instances associated with individual Subject principals. 092 */ 093 private boolean authorizationCachingEnabled; 094 private Cache<Object, AuthorizationInfo> authorizationCache; 095 private String authorizationCacheName; 096 097 private PermissionResolver permissionResolver; 098 099 private RolePermissionResolver permissionRoleResolver; 100 101 /*------------------------------------------- 102 | C O N S T R U C T O R S | 103 ============================================*/ 104 105 public AuthorizingRealm() { 106 this(null, null); 107 } 108 109 public AuthorizingRealm(CacheManager cacheManager) { 110 this(cacheManager, null); 111 } 112 113 public AuthorizingRealm(CredentialsMatcher matcher) { 114 this(null, matcher); 115 } 116 117 public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) { 118 super(); 119 if (cacheManager != null) { 120 setCacheManager(cacheManager); 121 } 122 if (matcher != null) { 123 setCredentialsMatcher(matcher); 124 } 125 126 this.authorizationCachingEnabled = true; 127 this.permissionResolver = new WildcardPermissionResolver(); 128 129 int instanceNumber = INSTANCE_COUNT.getAndIncrement(); 130 this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX; 131 if (instanceNumber > 0) { 132 this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber; 133 } 134 } 135 136 /*------------------------------------------- 137 | A C C E S S O R S / M O D I F I E R S | 138 ============================================*/ 139 140 public void setName(String name) { 141 super.setName(name); 142 String authzCacheName = this.authorizationCacheName; 143 if (authzCacheName != null && authzCacheName.startsWith(getClass().getName())) { 144 //get rid of the default class-name based cache name. Create a more meaningful one 145 //based on the application-unique Realm name: 146 this.authorizationCacheName = name + DEFAULT_AUTHORIZATION_CACHE_SUFFIX; 147 } 148 } 149 150 public void setAuthorizationCache(Cache<Object, AuthorizationInfo> authorizationCache) { 151 this.authorizationCache = authorizationCache; 152 } 153 154 public Cache<Object, AuthorizationInfo> getAuthorizationCache() { 155 return this.authorizationCache; 156 } 157 158 public String getAuthorizationCacheName() { 159 return authorizationCacheName; 160 } 161 162 @SuppressWarnings({"UnusedDeclaration"}) 163 public void setAuthorizationCacheName(String authorizationCacheName) { 164 this.authorizationCacheName = authorizationCacheName; 165 } 166 167 /** 168 * Returns {@code true} if authorization caching should be utilized if a {@link CacheManager} has been 169 * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise. 170 * <p/> 171 * The default value is {@code true}. 172 * 173 * @return {@code true} if authorization caching should be utilized, {@code false} otherwise. 174 */ 175 public boolean isAuthorizationCachingEnabled() { 176 return isCachingEnabled() && authorizationCachingEnabled; 177 } 178 179 /** 180 * Sets whether or not authorization caching should be utilized if a {@link CacheManager} has been 181 * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise. 182 * <p/> 183 * The default value is {@code true}. 184 * 185 * @param authorizationCachingEnabled the value to set 186 */ 187 @SuppressWarnings({"UnusedDeclaration"}) 188 public void setAuthorizationCachingEnabled(boolean authorizationCachingEnabled) { 189 this.authorizationCachingEnabled = authorizationCachingEnabled; 190 if (authorizationCachingEnabled) { 191 setCachingEnabled(true); 192 } 193 } 194 195 public PermissionResolver getPermissionResolver() { 196 return permissionResolver; 197 } 198 199 public void setPermissionResolver(PermissionResolver permissionResolver) { 200 if (permissionResolver == null) { 201 throw new IllegalArgumentException("Null PermissionResolver is not allowed"); 202 } 203 this.permissionResolver = permissionResolver; 204 } 205 206 public RolePermissionResolver getRolePermissionResolver() { 207 return permissionRoleResolver; 208 } 209 210 public void setRolePermissionResolver(RolePermissionResolver permissionRoleResolver) { 211 this.permissionRoleResolver = permissionRoleResolver; 212 } 213 214 /*-------------------------------------------- 215 | M E T H O D S | 216 ============================================*/ 217 218 /** 219 * Initializes this realm and potentially enables a cache, depending on configuration. 220 * <p/> 221 * When this method is called, the following logic is executed: 222 * <ol> 223 * <li>If the {@link #setAuthorizationCache cache} property has been set, it will be 224 * used to cache the AuthorizationInfo objects returned from {@link #getAuthorizationInfo} 225 * method invocations. 226 * All future calls to {@code getAuthorizationInfo} will attempt to use this cache first 227 * to alleviate any potentially unnecessary calls to an underlying data store.</li> 228 * <li>If the {@link #setAuthorizationCache cache} property has <b>not</b> been set, 229 * the {@link #setCacheManager cacheManager} property will be checked. 230 * If a {@code cacheManager} has been set, it will be used to create an authorization 231 * {@code cache}, and this newly created cache which will be used as specified in #1.</li> 232 * <li>If neither the {@link #setAuthorizationCache (org.apache.shiro.cache.Cache) cache} 233 * or {@link #setCacheManager(org.apache.shiro.cache.CacheManager) cacheManager} 234 * properties are set, caching will be disabled and authorization look-ups will be delegated to 235 * subclass implementations for each authorization check.</li> 236 * </ol> 237 */ 238 protected void onInit() { 239 super.onInit(); 240 //trigger obtaining the authorization cache if possible 241 getAvailableAuthorizationCache(); 242 } 243 244 protected void afterCacheManagerSet() { 245 super.afterCacheManagerSet(); 246 //trigger obtaining the authorization cache if possible 247 getAvailableAuthorizationCache(); 248 } 249 250 private Cache<Object, AuthorizationInfo> getAuthorizationCacheLazy() { 251 252 if (this.authorizationCache == null) { 253 254 if (LOGGER.isDebugEnabled()) { 255 LOGGER.debug("No authorizationCache instance set. Checking for a cacheManager..."); 256 } 257 258 CacheManager cacheManager = getCacheManager(); 259 260 if (cacheManager != null) { 261 String cacheName = getAuthorizationCacheName(); 262 if (LOGGER.isDebugEnabled()) { 263 LOGGER.debug("CacheManager [" + cacheManager + "] has been configured. Building " 264 + "authorization cache named [" + cacheName + "]"); 265 } 266 this.authorizationCache = cacheManager.getCache(cacheName); 267 } else { 268 if (LOGGER.isDebugEnabled()) { 269 LOGGER.debug("No cache or cacheManager properties have been set. Authorization cache cannot " 270 + "be obtained."); 271 } 272 } 273 } 274 275 return this.authorizationCache; 276 } 277 278 private Cache<Object, AuthorizationInfo> getAvailableAuthorizationCache() { 279 Cache<Object, AuthorizationInfo> cache = getAuthorizationCache(); 280 if (cache == null && isAuthorizationCachingEnabled()) { 281 cache = getAuthorizationCacheLazy(); 282 } 283 return cache; 284 } 285 286 /** 287 * Returns an account's authorization-specific information for the specified {@code principals}, 288 * or {@code null} if no account could be found. The resulting {@code AuthorizationInfo} object is used 289 * by the other method implementations in this class to automatically perform access control checks for the 290 * corresponding {@code Subject}. 291 * <p/> 292 * This implementation obtains the actual {@code AuthorizationInfo} object from the subclass's 293 * implementation of 294 * {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) doGetAuthorizationInfo}, and then 295 * caches it for efficient reuse if caching is enabled (see below). 296 * <p/> 297 * Invocations of this method should be thought of as completely orthogonal to acquiring 298 * {@link #getAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) authenticationInfo}, since either could 299 * occur in any order. 300 * <p/> 301 * For example, in "Remember Me" scenarios, the user identity is remembered (and 302 * assumed) for their current session and an authentication attempt during that session might never occur. 303 * But because their identity would be remembered, that is sufficient enough information to call this method to 304 * execute any necessary authorization checks. For this reason, authentication and authorization should be 305 * loosely coupled and not depend on each other. 306 * <h3>Caching</h3> 307 * The {@code AuthorizationInfo} values returned from this method are cached for efficient reuse 308 * if caching is enabled. Caching is enabled automatically when an {@link #setAuthorizationCache authorizationCache} 309 * instance has been explicitly configured, or if a {@link #setCacheManager cacheManager} has been configured, which 310 * will be used to lazily create the {@code authorizationCache} as needed. 311 * <p/> 312 * If caching is enabled, the authorization cache will be checked first and if found, will return the cached 313 * {@code AuthorizationInfo} immediately. If caching is disabled, or there is a cache miss, the authorization 314 * info will be looked up from the underlying data store via the 315 * {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} method, which must be implemented 316 * by subclasses. 317 * <h4>Changed Data</h4> 318 * If caching is enabled and if any authorization data for an account is changed at 319 * runtime, such as adding or removing roles and/or permissions, the subclass implementation should clear the 320 * cached AuthorizationInfo for that account via the 321 * {@link #clearCachedAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) clearCachedAuthorizationInfo} 322 * method. This ensures that the next call to {@code getAuthorizationInfo(PrincipalCollection)} will 323 * acquire the account's fresh authorization data, where it will then be cached for efficient reuse. This 324 * ensures that stale authorization data will not be reused. 325 * 326 * @param principals the corresponding Subject's identifying principals with which to look up the Subject's 327 * {@code AuthorizationInfo}. 328 * @return the authorization information for the account associated with the specified {@code principals}, 329 * or {@code null} if no account could be found. 330 */ 331 protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { 332 333 if (principals == null) { 334 return null; 335 } 336 337 AuthorizationInfo info = null; 338 339 if (LOGGER.isTraceEnabled()) { 340 LOGGER.trace("Retrieving AuthorizationInfo for principals [" + principals + "]"); 341 } 342 343 Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache(); 344 if (cache != null) { 345 if (LOGGER.isTraceEnabled()) { 346 LOGGER.trace("Attempting to retrieve the AuthorizationInfo from cache."); 347 } 348 Object key = getAuthorizationCacheKey(principals); 349 info = cache.get(key); 350 if (LOGGER.isTraceEnabled()) { 351 if (info == null) { 352 LOGGER.trace("No AuthorizationInfo found in cache for principals [" + principals + "]"); 353 } else { 354 LOGGER.trace("AuthorizationInfo found in cache for principals [" + principals + "]"); 355 } 356 } 357 } 358 359 360 if (info == null) { 361 // Call template method if the info was not found in a cache 362 info = doGetAuthorizationInfo(principals); 363 // If the info is not null and the cache has been created, then cache the authorization info. 364 if (info != null && cache != null) { 365 if (LOGGER.isTraceEnabled()) { 366 LOGGER.trace("Caching authorization info for principals: [" + principals + "]."); 367 } 368 Object key = getAuthorizationCacheKey(principals); 369 cache.put(key, info); 370 } 371 } 372 373 return info; 374 } 375 376 protected Object getAuthorizationCacheKey(PrincipalCollection principals) { 377 return principals; 378 } 379 380 /** 381 * Clears out the AuthorizationInfo cache entry for the specified account. 382 * <p/> 383 * This method is provided as a convenience to subclasses so they can invalidate a cache entry when they 384 * change an account's authorization data (add/remove roles or permissions) during runtime. Because an account's 385 * AuthorizationInfo can be cached, there needs to be a way to invalidate the cache for only that account so that 386 * subsequent authorization operations don't used the (old) cached value if account data changes. 387 * <p/> 388 * After this method is called, the next authorization check for that same account will result in a call to 389 * {@link #getAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) getAuthorizationInfo}, and the 390 * resulting return value will be cached before being returned so it can be reused for later authorization checks. 391 * <p/> 392 * If you wish to clear out all associated cached data (and not just authorization data), use the 393 * {@link #clearCache(org.apache.shiro.subject.PrincipalCollection)} method instead (which will in turn call this 394 * method by default). 395 * 396 * @param principals the principals of the account for which to clear the cached AuthorizationInfo. 397 */ 398 protected void clearCachedAuthorizationInfo(PrincipalCollection principals) { 399 if (principals == null) { 400 return; 401 } 402 403 Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache(); 404 //cache instance will be non-null if caching is enabled: 405 if (cache != null) { 406 Object key = getAuthorizationCacheKey(principals); 407 cache.remove(key); 408 } 409 } 410 411 /** 412 * Retrieves the AuthorizationInfo for the given principals from the underlying data store. When returning 413 * an instance from this method, you might want to consider using an instance of 414 * {@link org.apache.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}, as it is suitable in most cases. 415 * 416 * @param principals the primary identifying principals of the AuthorizationInfo that should be retrieved. 417 * @return the AuthorizationInfo associated with this principals. 418 * @see org.apache.shiro.authz.SimpleAuthorizationInfo 419 */ 420 protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals); 421 422 //visibility changed from private to protected per SHIRO-332 423 protected Collection<Permission> getPermissions(AuthorizationInfo info) { 424 Set<Permission> permissions = new HashSet<Permission>(); 425 426 if (info != null) { 427 Collection<Permission> perms = info.getObjectPermissions(); 428 if (!CollectionUtils.isEmpty(perms)) { 429 permissions.addAll(perms); 430 } 431 perms = resolvePermissions(info.getStringPermissions()); 432 if (!CollectionUtils.isEmpty(perms)) { 433 permissions.addAll(perms); 434 } 435 436 perms = resolveRolePermissions(info.getRoles()); 437 if (!CollectionUtils.isEmpty(perms)) { 438 permissions.addAll(perms); 439 } 440 } 441 442 if (permissions.isEmpty()) { 443 return Collections.emptySet(); 444 } else { 445 return Collections.unmodifiableSet(permissions); 446 } 447 } 448 449 private Collection<Permission> resolvePermissions(Collection<String> stringPerms) { 450 Collection<Permission> perms = Collections.emptySet(); 451 PermissionResolver resolver = getPermissionResolver(); 452 if (resolver != null && !CollectionUtils.isEmpty(stringPerms)) { 453 perms = new LinkedHashSet<Permission>(stringPerms.size()); 454 for (String strPermission : stringPerms) { 455 if (StringUtils.clean(strPermission) != null) { 456 Permission permission = resolver.resolvePermission(strPermission); 457 perms.add(permission); 458 } 459 } 460 } 461 return perms; 462 } 463 464 private Collection<Permission> resolveRolePermissions(Collection<String> roleNames) { 465 Collection<Permission> perms = Collections.emptySet(); 466 RolePermissionResolver resolver = getRolePermissionResolver(); 467 if (resolver != null && !CollectionUtils.isEmpty(roleNames)) { 468 perms = new LinkedHashSet<Permission>(roleNames.size()); 469 for (String roleName : roleNames) { 470 Collection<Permission> resolved = resolver.resolvePermissionsInRole(roleName); 471 if (!CollectionUtils.isEmpty(resolved)) { 472 perms.addAll(resolved); 473 } 474 } 475 } 476 return perms; 477 } 478 479 public boolean isPermitted(PrincipalCollection principals, String permission) { 480 Permission p = getPermissionResolver().resolvePermission(permission); 481 return isPermitted(principals, p); 482 } 483 484 public boolean isPermitted(PrincipalCollection principals, Permission permission) { 485 AuthorizationInfo info = getAuthorizationInfo(principals); 486 return isPermitted(permission, info); 487 } 488 489 //visibility changed from private to protected per SHIRO-332 490 protected boolean isPermitted(Permission permission, AuthorizationInfo info) { 491 Collection<Permission> perms = getPermissions(info); 492 if (perms != null && !perms.isEmpty()) { 493 for (Permission perm : perms) { 494 if (perm.implies(permission)) { 495 return true; 496 } 497 } 498 } 499 return false; 500 } 501 502 public boolean[] isPermitted(PrincipalCollection subjectIdentifier, String... permissions) { 503 List<Permission> perms = new ArrayList<Permission>(permissions.length); 504 for (String permString : permissions) { 505 perms.add(getPermissionResolver().resolvePermission(permString)); 506 } 507 return isPermitted(subjectIdentifier, perms); 508 } 509 510 public boolean[] isPermitted(PrincipalCollection principals, List<Permission> permissions) { 511 AuthorizationInfo info = getAuthorizationInfo(principals); 512 return isPermitted(permissions, info); 513 } 514 515 protected boolean[] isPermitted(List<Permission> permissions, AuthorizationInfo info) { 516 boolean[] result; 517 if (permissions != null && !permissions.isEmpty()) { 518 int size = permissions.size(); 519 result = new boolean[size]; 520 int i = 0; 521 for (Permission p : permissions) { 522 result[i++] = isPermitted(p, info); 523 } 524 } else { 525 result = new boolean[0]; 526 } 527 return result; 528 } 529 530 public boolean isPermittedAll(PrincipalCollection subjectIdentifier, String... permissions) { 531 if (permissions != null && permissions.length > 0) { 532 Collection<Permission> perms = new ArrayList<Permission>(permissions.length); 533 for (String permString : permissions) { 534 perms.add(getPermissionResolver().resolvePermission(permString)); 535 } 536 return isPermittedAll(subjectIdentifier, perms); 537 } 538 return false; 539 } 540 541 public boolean isPermittedAll(PrincipalCollection principal, Collection<Permission> permissions) { 542 AuthorizationInfo info = getAuthorizationInfo(principal); 543 return info != null && isPermittedAll(permissions, info); 544 } 545 546 protected boolean isPermittedAll(Collection<Permission> permissions, AuthorizationInfo info) { 547 if (permissions != null && !permissions.isEmpty()) { 548 for (Permission p : permissions) { 549 if (!isPermitted(p, info)) { 550 return false; 551 } 552 } 553 } 554 return true; 555 } 556 557 public void checkPermission(PrincipalCollection subjectIdentifier, String permission) throws AuthorizationException { 558 Permission p = getPermissionResolver().resolvePermission(permission); 559 checkPermission(subjectIdentifier, p); 560 } 561 562 public void checkPermission(PrincipalCollection principal, Permission permission) throws AuthorizationException { 563 AuthorizationInfo info = getAuthorizationInfo(principal); 564 checkPermission(permission, info); 565 } 566 567 protected void checkPermission(Permission permission, AuthorizationInfo info) { 568 if (!isPermitted(permission, info)) { 569 String msg = "User is not permitted [" + permission + "]"; 570 throw new UnauthorizedException(msg); 571 } 572 } 573 574 public void checkPermissions(PrincipalCollection subjectIdentifier, String... permissions) throws AuthorizationException { 575 if (permissions != null) { 576 for (String permString : permissions) { 577 checkPermission(subjectIdentifier, permString); 578 } 579 } 580 } 581 582 public void checkPermissions(PrincipalCollection principal, 583 Collection<Permission> permissions) throws AuthorizationException { 584 AuthorizationInfo info = getAuthorizationInfo(principal); 585 checkPermissions(permissions, info); 586 } 587 588 protected void checkPermissions(Collection<Permission> permissions, AuthorizationInfo info) { 589 if (permissions != null && !permissions.isEmpty()) { 590 for (Permission p : permissions) { 591 checkPermission(p, info); 592 } 593 } 594 } 595 596 public boolean hasRole(PrincipalCollection principal, String roleIdentifier) { 597 AuthorizationInfo info = getAuthorizationInfo(principal); 598 return hasRole(roleIdentifier, info); 599 } 600 601 protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) { 602 return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier); 603 } 604 605 public boolean[] hasRoles(PrincipalCollection principal, List<String> roleIdentifiers) { 606 AuthorizationInfo info = getAuthorizationInfo(principal); 607 boolean[] result = new boolean[roleIdentifiers != null ? roleIdentifiers.size() : 0]; 608 if (info != null) { 609 result = hasRoles(roleIdentifiers, info); 610 } 611 return result; 612 } 613 614 protected boolean[] hasRoles(List<String> roleIdentifiers, AuthorizationInfo info) { 615 boolean[] result; 616 if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) { 617 int size = roleIdentifiers.size(); 618 result = new boolean[size]; 619 int i = 0; 620 for (String roleName : roleIdentifiers) { 621 result[i++] = hasRole(roleName, info); 622 } 623 } else { 624 result = new boolean[0]; 625 } 626 return result; 627 } 628 629 public boolean hasAllRoles(PrincipalCollection principal, Collection<String> roleIdentifiers) { 630 AuthorizationInfo info = getAuthorizationInfo(principal); 631 return info != null && hasAllRoles(roleIdentifiers, info); 632 } 633 634 private boolean hasAllRoles(Collection<String> roleIdentifiers, AuthorizationInfo info) { 635 if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) { 636 for (String roleName : roleIdentifiers) { 637 if (!hasRole(roleName, info)) { 638 return false; 639 } 640 } 641 } 642 return true; 643 } 644 645 public void checkRole(PrincipalCollection principal, String role) throws AuthorizationException { 646 AuthorizationInfo info = getAuthorizationInfo(principal); 647 checkRole(role, info); 648 } 649 650 protected void checkRole(String role, AuthorizationInfo info) { 651 if (!hasRole(role, info)) { 652 String msg = "User does not have role [" + role + "]"; 653 throw new UnauthorizedException(msg); 654 } 655 } 656 657 public void checkRoles(PrincipalCollection principal, Collection<String> roles) throws AuthorizationException { 658 AuthorizationInfo info = getAuthorizationInfo(principal); 659 checkRoles(roles, info); 660 } 661 662 public void checkRoles(PrincipalCollection principal, String... roles) throws AuthorizationException { 663 checkRoles(principal, Arrays.asList(roles)); 664 } 665 666 protected void checkRoles(Collection<String> roles, AuthorizationInfo info) { 667 if (roles != null && !roles.isEmpty()) { 668 for (String roleName : roles) { 669 checkRole(roleName, info); 670 } 671 } 672 } 673 674 /** 675 * Calls {@code super.doClearCache} to ensure any cached authentication data is removed and then calls 676 * {@link #clearCachedAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} to remove any cached 677 * authorization data. 678 * <p/> 679 * If overriding in a subclass, be sure to call {@code super.doClearCache} to ensure this behavior is maintained. 680 * 681 * @param principals the principals of the account for which to clear any cached AuthorizationInfo 682 * @since 1.2 683 */ 684 @Override 685 protected void doClearCache(PrincipalCollection principals) { 686 super.doClearCache(principals); 687 clearCachedAuthorizationInfo(principals); 688 } 689}