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