1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module hunt.shiro.realm.AuthorizingRealm; 20 21 import hunt.shiro.realm.AuthenticatingRealm; 22 23 import hunt.shiro.authc.AuthenticationInfo; 24 import hunt.shiro.authc.AuthenticationToken; 25 import hunt.shiro.authc.credential.CredentialsMatcher; 26 import hunt.shiro.authz; 27 import hunt.shiro.authz.permission; 28 import hunt.shiro.cache.AbstractCacheManager; 29 import hunt.shiro.cache.Cache; 30 import hunt.shiro.cache.CacheManager; 31 import hunt.shiro.Exceptions; 32 import hunt.shiro.subject.PrincipalCollection; 33 import hunt.shiro.util.CollectionUtils; 34 import hunt.shiro.util.Common; 35 36 import hunt.collection; 37 import hunt.Exceptions; 38 import hunt.logging.Logger; 39 40 import core.atomic; 41 import std.conv; 42 import std.string; 43 44 45 /** 46 * An {@code AuthorizingRealm} extends the {@code AuthenticatingRealm}'s capabilities by adding Authorization 47 * (access control) support. 48 * <p/> 49 * This implementation will perform all role and permission checks automatically (and subclasses do not have to 50 * write this logic) as long as the 51 * {@link #getAuthorizationInfo(hunt.shiro.subject.PrincipalCollection)} method returns an 52 * {@link AuthorizationInfo}. Please see that method's JavaDoc for an in-depth explanation. 53 * <p/> 54 * If you find that you do not want to utilize the {@link AuthorizationInfo AuthorizationInfo} construct, 55 * you are of course free to subclass the {@link AuthenticatingRealm AuthenticatingRealm} directly instead and 56 * implement the remaining Realm interface methods directly. You might do this if you want have better control 57 * over how the Role and Permission checks occur for your specific data source. However, using AuthorizationInfo 58 * (and its default implementation {@link hunt.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}) is sufficient in the large 59 * majority of Realm cases. 60 * 61 * @see hunt.shiro.authz.SimpleAuthorizationInfo 62 */ 63 abstract class AuthorizingRealm : AuthenticatingRealm, 64 Authorizer, PermissionResolverAware, RolePermissionResolverAware { 65 66 67 /*------------------------------------------- 68 | C O N S T A N T S | 69 ============================================*/ 70 71 72 /** 73 * The default suffix appended to the realm name for caching AuthorizationInfo instances. 74 */ 75 private enum string DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authorizationCache"; 76 77 private static shared int INSTANCE_COUNT = 0; 78 79 /*------------------------------------------- 80 | I N S T A N C E V A R I A B L E S | 81 ============================================*/ 82 /** 83 * The cache used by this realm to store AuthorizationInfo instances associated with individual Subject principals. 84 */ 85 private bool authorizationCachingEnabled; 86 private Cache!(Object, AuthorizationInfo) authorizationCache; 87 private string authorizationCacheName; 88 89 private PermissionResolver permissionResolver; 90 91 private RolePermissionResolver permissionRoleResolver; 92 93 /*------------------------------------------- 94 | C O N S T R U C T O R S | 95 ============================================*/ 96 97 this() { 98 this(null, null); 99 } 100 101 this(CacheManager cacheManager) { 102 this(cacheManager, null); 103 } 104 105 this(CredentialsMatcher matcher) { 106 this(null, matcher); 107 } 108 109 this(CacheManager cacheManager, CredentialsMatcher matcher) { 110 super(); 111 if (cacheManager !is null) setCacheManager(cacheManager); 112 if (matcher !is null) setCredentialsMatcher(matcher); 113 114 this.authorizationCachingEnabled = true; 115 this.permissionResolver = new WildcardPermissionResolver(); 116 117 118 int instanceNumber = atomicOp!("+=")(INSTANCE_COUNT, 1); 119 instanceNumber--; 120 this.authorizationCacheName = typeid(this).name ~ DEFAULT_AUTHORIZATION_CACHE_SUFFIX; 121 if (instanceNumber > 0) { 122 this.authorizationCacheName = this.authorizationCacheName ~ "." ~ instanceNumber.to!string(); 123 } 124 } 125 126 /*------------------------------------------- 127 | A C C E S S O R S / M O D I F I E R S | 128 ============================================*/ 129 130 override void setName(string name) { 131 super.setName(name); 132 string authzCacheName = this.authorizationCacheName; 133 if (authzCacheName !is null && authzCacheName.startsWith(typeid(this).name)) { 134 //get rid of the default class-name based cache name. Create a more meaningful one 135 //based on the application-unique Realm name: 136 this.authorizationCacheName = name ~ DEFAULT_AUTHORIZATION_CACHE_SUFFIX; 137 } 138 } 139 140 void setAuthorizationCache(Cache!(Object, AuthorizationInfo) authorizationCache) { 141 this.authorizationCache = authorizationCache; 142 } 143 144 Cache!(Object, AuthorizationInfo) getAuthorizationCache() { 145 return this.authorizationCache; 146 } 147 148 string getAuthorizationCacheName() { 149 return authorizationCacheName; 150 } 151 152 //@SuppressWarnings({"UnusedDeclaration"}) 153 void setAuthorizationCacheName(string authorizationCacheName) { 154 this.authorizationCacheName = authorizationCacheName; 155 } 156 157 /** 158 * Returns {@code true} if authorization caching should be utilized if a {@link CacheManager} has been 159 * {@link #setCacheManager(hunt.shiro.cache.CacheManager) configured}, {@code false} otherwise. 160 * <p/> 161 * The default value is {@code true}. 162 * 163 * @return {@code true} if authorization caching should be utilized, {@code false} otherwise. 164 */ 165 bool isAuthorizationCachingEnabled() { 166 version(HUNT_SHIRO_DEBUG) { 167 tracef("authorizationCachingEnabled=%s, isCachingEnabled=%s", 168 authorizationCachingEnabled, isCachingEnabled()); 169 } 170 return isCachingEnabled() && authorizationCachingEnabled; 171 } 172 173 /** 174 * Sets whether or not authorization caching should be utilized if a {@link CacheManager} has been 175 * {@link #setCacheManager(hunt.shiro.cache.CacheManager) configured}, {@code false} otherwise. 176 * <p/> 177 * The default value is {@code true}. 178 * 179 * @param authenticationCachingEnabled the value to set 180 */ 181 //@SuppressWarnings({"UnusedDeclaration"}) 182 void setAuthorizationCachingEnabled(bool authenticationCachingEnabled) { 183 this.authorizationCachingEnabled = authenticationCachingEnabled; 184 if (authenticationCachingEnabled) { 185 setCachingEnabled(true); 186 } 187 } 188 189 PermissionResolver getPermissionResolver() { 190 return permissionResolver; 191 } 192 193 void setPermissionResolver(PermissionResolver permissionResolver) { 194 if (permissionResolver is null) throw new IllegalArgumentException("Null PermissionResolver is not allowed"); 195 this.permissionResolver = permissionResolver; 196 } 197 198 RolePermissionResolver getRolePermissionResolver() { 199 return permissionRoleResolver; 200 } 201 202 void setRolePermissionResolver(RolePermissionResolver permissionRoleResolver) { 203 this.permissionRoleResolver = permissionRoleResolver; 204 } 205 206 /*-------------------------------------------- 207 | M E T H O D S | 208 ============================================*/ 209 210 /** 211 * Initializes this realm and potentially enables a cache, depending on configuration. 212 * <p/> 213 * When this method is called, the following logic is executed: 214 * <ol> 215 * <li>If the {@link #setAuthorizationCache cache} property has been set, it will be 216 * used to cache the AuthorizationInfo objects returned from {@link #getAuthorizationInfo} 217 * method invocations. 218 * All future calls to {@code getAuthorizationInfo} will attempt to use this cache first 219 * to alleviate any potentially unnecessary calls to an underlying data store.</li> 220 * <li>If the {@link #setAuthorizationCache cache} property has <b>not</b> been set, 221 * the {@link #setCacheManager cacheManager} property will be checked. 222 * If a {@code cacheManager} has been set, it will be used to create an authorization 223 * {@code cache}, and this newly created cache which will be used as specified in #1.</li> 224 * <li>If neither the {@link #setAuthorizationCache (hunt.shiro.cache.Cache) cache} 225 * or {@link #setCacheManager(hunt.shiro.cache.CacheManager) cacheManager} 226 * properties are set, caching will be disabled and authorization look-ups will be delegated to 227 * subclass implementations for each authorization check.</li> 228 * </ol> 229 */ 230 override protected void onInit() { 231 super.onInit(); 232 //trigger obtaining the authorization cache if possible 233 getAvailableAuthorizationCache(); 234 } 235 236 override protected void afterCacheManagerSet() { 237 super.afterCacheManagerSet(); 238 //trigger obtaining the authorization cache if possible 239 getAvailableAuthorizationCache(); 240 } 241 242 private Cache!(Object, AuthorizationInfo) getAuthorizationCacheLazy() { 243 244 if (this.authorizationCache is null) { 245 246 version(HUNT_SHIRO_DEBUG) 247 { 248 tracef("No authorizationCache instance set. Checking for a cacheManager..."); 249 } 250 251 CacheManager cacheManager = getCacheManager(); 252 253 if (cacheManager !is null) { 254 string cacheName = getAuthorizationCacheName(); 255 version(HUNT_SHIRO_DEBUG) 256 { 257 tracef("CacheManager [" ~ (cast(Object)cacheManager).toString() ~ 258 "] has been configured. Building " ~ 259 "authorization cache named [" ~ cacheName ~ "]"); 260 } 261 auto acm = cast(AbstractCacheManager!(Object, AuthorizationInfo))cacheManager; 262 this.authorizationCache = acm.getCache(cacheName); 263 264 version(HUNT_SHIRO_DEBUG) tracef("authorizationCache: %s", this.authorizationCache is null); 265 } else { 266 version(HUNT_SHIRO_DEBUG) 267 { 268 tracef("No cache or cacheManager properties have been set. Authorization cache cannot " ~ 269 "be obtained."); 270 } 271 } 272 } 273 274 return this.authorizationCache; 275 } 276 277 private Cache!(Object, AuthorizationInfo) getAvailableAuthorizationCache() { 278 Cache!(Object, AuthorizationInfo) cache = getAuthorizationCache(); 279 if (cache is null && isAuthorizationCachingEnabled()) { 280 cache = getAuthorizationCacheLazy(); 281 } 282 return cache; 283 } 284 285 /** 286 * Returns an account's authorization-specific information for the specified {@code principals}, 287 * or {@code null} if no account could be found. The resulting {@code AuthorizationInfo} object is used 288 * by the other method implementations in this class to automatically perform access control checks for the 289 * corresponding {@code Subject}. 290 * <p/> 291 * This implementation obtains the actual {@code AuthorizationInfo} object from the subclass's 292 * implementation of 293 * {@link #doGetAuthorizationInfo(hunt.shiro.subject.PrincipalCollection) doGetAuthorizationInfo}, and then 294 * caches it for efficient reuse if caching is enabled (see below). 295 * <p/> 296 * Invocations of this method should be thought of as completely orthogonal to acquiring 297 * {@link #getAuthenticationInfo(hunt.shiro.authc.AuthenticationToken) authenticationInfo}, since either could 298 * occur in any order. 299 * <p/> 300 * For example, in "Remember Me" scenarios, the user identity is remembered (and 301 * assumed) for their current session and an authentication attempt during that session might never occur. 302 * But because their identity would be remembered, that is sufficient enough information to call this method to 303 * execute any necessary authorization checks. For this reason, authentication and authorization should be 304 * loosely coupled and not depend on each other. 305 * <h3>Caching</h3> 306 * The {@code AuthorizationInfo} values returned from this method are cached for efficient reuse 307 * if caching is enabled. Caching is enabled automatically when an {@link #setAuthorizationCache authorizationCache} 308 * instance has been explicitly configured, or if a {@link #setCacheManager cacheManager} has been configured, which 309 * will be used to lazily create the {@code authorizationCache} as needed. 310 * <p/> 311 * If caching is enabled, the authorization cache will be checked first and if found, will return the cached 312 * {@code AuthorizationInfo} immediately. If caching is disabled, or there is a cache miss, the authorization 313 * info will be looked up from the underlying data store via the 314 * {@link #doGetAuthorizationInfo(hunt.shiro.subject.PrincipalCollection)} method, which must be implemented 315 * by subclasses. 316 * <h4>Changed Data</h4> 317 * If caching is enabled and if any authorization data for an account is changed at 318 * runtime, such as adding or removing roles and/or permissions, the subclass implementation should clear the 319 * cached AuthorizationInfo for that account via the 320 * {@link #clearCachedAuthorizationInfo(hunt.shiro.subject.PrincipalCollection) clearCachedAuthorizationInfo} 321 * method. This ensures that the next call to {@code getAuthorizationInfo(PrincipalCollection)} will 322 * acquire the account's fresh authorization data, where it will then be cached for efficient reuse. This 323 * ensures that stale authorization data will not be reused. 324 * 325 * @param principals the corresponding Subject's identifying principals with which to look up the Subject's 326 * {@code AuthorizationInfo}. 327 * @return the authorization information for the account associated with the specified {@code principals}, 328 * or {@code null} if no account could be found. 329 */ 330 protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { 331 332 if (principals is null) { 333 return null; 334 } 335 336 AuthorizationInfo info = null; 337 338 version(HUNT_SHIRO_DEBUG_MORE) { 339 tracef("Retrieving AuthorizationInfo for principals [" ~ (cast(Object)principals).toString() ~ "]"); 340 } 341 342 Cache!(Object, AuthorizationInfo) cache = getAvailableAuthorizationCache(); 343 344 if (cache !is null) { 345 version(HUNT_SHIRO_DEBUG_MORE) { 346 tracef("Attempting to retrieve the AuthorizationInfo from cache."); 347 } 348 Object key = getAuthorizationCacheKey(principals); 349 info = cache.get(key, null); 350 version(HUNT_SHIRO_DEBUG_MORE) { 351 if (info is null) { 352 tracef("No AuthorizationInfo found in cache for principals [" ~ 353 (cast(Object)principals).toString() ~ "]"); 354 } else { 355 tracef("AuthorizationInfo found in cache for principals [" ~ 356 (cast(Object)principals).toString() ~ "]"); 357 } 358 } 359 } 360 361 362 if (info is null) { 363 // Call template method if the info was not found in a cache 364 info = doGetAuthorizationInfo(principals); 365 // If the info is not null and the cache has been created, then cache the authorization info. 366 if (info !is null && cache !is null) { 367 version(HUNT_SHIRO_DEBUG) { 368 tracef("Caching authorization info for principals: [" ~ 369 (cast(Object)principals).toString() ~ "]."); 370 } 371 Object key = getAuthorizationCacheKey(principals); 372 cache.put(key, info); 373 } 374 } 375 376 return info; 377 } 378 379 protected Object getAuthorizationCacheKey(PrincipalCollection principals) { 380 return cast(Object)principals; 381 } 382 383 /** 384 * Clears out the AuthorizationInfo cache entry for the specified account. 385 * <p/> 386 * This method is provided as a convenience to subclasses so they can invalidate a cache entry when they 387 * change an account's authorization data (add/remove roles or permissions) during runtime. Because an account's 388 * AuthorizationInfo can be cached, there needs to be a way to invalidate the cache for only that account so that 389 * subsequent authorization operations don't used the (old) cached value if account data changes. 390 * <p/> 391 * After this method is called, the next authorization check for that same account will result in a call to 392 * {@link #getAuthorizationInfo(hunt.shiro.subject.PrincipalCollection) getAuthorizationInfo}, and the 393 * resulting return value will be cached before being returned so it can be reused for later authorization checks. 394 * <p/> 395 * If you wish to clear out all associated cached data (and not just authorization data), use the 396 * {@link #clearCache(hunt.shiro.subject.PrincipalCollection)} method instead (which will in turn call this 397 * method by default). 398 * 399 * @param principals the principals of the account for which to clear the cached AuthorizationInfo. 400 */ 401 protected void clearCachedAuthorizationInfo(PrincipalCollection principals) { 402 if (principals is null) { 403 return; 404 } 405 406 Cache!(Object, AuthorizationInfo) cache = getAvailableAuthorizationCache(); 407 //cache instance will be non-null if caching is enabled: 408 if (cache !is null) { 409 Object key = getAuthorizationCacheKey(principals); 410 cache.remove(key); 411 } 412 } 413 414 /** 415 * Retrieves the AuthorizationInfo for the given principals from the underlying data store. When returning 416 * an instance from this method, you might want to consider using an instance of 417 * {@link hunt.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}, as it is suitable in most cases. 418 * 419 * @param principals the primary identifying principals of the AuthorizationInfo that should be retrieved. 420 * @return the AuthorizationInfo associated with this principals. 421 * @see hunt.shiro.authz.SimpleAuthorizationInfo 422 */ 423 protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals); 424 425 //visibility changed from private to protected per SHIRO-332 426 protected Collection!(Permission) getPermissions(AuthorizationInfo info) { 427 Set!(Permission) permissions = new HashSet!(Permission)(); 428 429 if (info !is null) { 430 Collection!(Permission) perms = info.getObjectPermissions(); 431 if (!CollectionUtils.isEmpty(perms)) { 432 permissions.addAll(perms); 433 } 434 perms = resolvePermissions(info.getStringPermissions()); 435 if (!CollectionUtils.isEmpty(perms)) { 436 permissions.addAll(perms); 437 } 438 439 perms = resolveRolePermissions(info.getRoles()); 440 if (!CollectionUtils.isEmpty(perms)) { 441 permissions.addAll(perms); 442 } 443 } 444 445 if (permissions.isEmpty()) { 446 return Collections.emptySet!(Permission)(); 447 } else { 448 return permissions; 449 } 450 } 451 452 private Collection!(Permission) resolvePermissions(Collection!(string) stringPerms) { 453 Collection!(Permission) perms = Collections.emptySet!Permission(); 454 PermissionResolver resolver = getPermissionResolver(); 455 if (resolver !is null && !CollectionUtils.isEmpty(stringPerms)) { 456 perms = new LinkedHashSet!(Permission)(stringPerms.size()); 457 foreach(string strPermission ; stringPerms) { 458 Permission permission = resolver.resolvePermission(strPermission); 459 perms.add(permission); 460 } 461 } 462 return perms; 463 } 464 465 private Collection!(Permission) resolveRolePermissions(Collection!(string) roleNames) { 466 Collection!(Permission) perms = Collections.emptySet!Permission(); 467 RolePermissionResolver resolver = getRolePermissionResolver(); 468 if (resolver !is null && !CollectionUtils.isEmpty(roleNames)) { 469 perms = new LinkedHashSet!(Permission)(roleNames.size()); 470 foreach(string roleName ; roleNames) { 471 Collection!(Permission) resolved = resolver.resolvePermissionsInRole(roleName); 472 if (!CollectionUtils.isEmpty(resolved)) { 473 perms.addAll(resolved); 474 } 475 } 476 } 477 return perms; 478 } 479 480 bool isPermitted(PrincipalCollection principals, string permission) { 481 try { 482 Permission p = getPermissionResolver().resolvePermission(permission); 483 return isPermitted(principals, p); 484 } catch(Exception ex) { 485 warning(ex.msg); 486 version(HUNT_DEBUG) warning(ex); 487 return false; 488 } 489 } 490 491 bool isPermitted(PrincipalCollection principals, Permission permission) { 492 try { 493 AuthorizationInfo info = getAuthorizationInfo(principals); 494 return isPermitted(permission, info); 495 } catch(Exception ex) { 496 warning(ex.msg); 497 version(HUNT_SHIRO_DEBUG) warning(ex); 498 return false; 499 } 500 } 501 502 //visibility changed from private to protected per SHIRO-332 503 protected bool isPermitted(Permission permission, AuthorizationInfo info) { 504 Collection!(Permission) perms = getPermissions(info); 505 if (perms !is null && !perms.isEmpty()) { 506 foreach (Permission perm; perms) { 507 if (perm.implies(permission)) { 508 return true; 509 } 510 } 511 } 512 return false; 513 } 514 515 bool[] isPermitted(PrincipalCollection subjectIdentifier, string[] permissions...) { 516 List!(Permission) perms = new ArrayList!(Permission)(cast(int) permissions.length); 517 foreach (string permString; permissions) { 518 perms.add(getPermissionResolver().resolvePermission(permString)); 519 } 520 return isPermitted(subjectIdentifier, perms); 521 } 522 523 bool[] isPermitted(PrincipalCollection principals, List!(Permission) permissions) { 524 AuthorizationInfo info = getAuthorizationInfo(principals); 525 return isPermitted(permissions, info); 526 } 527 528 protected bool[] isPermitted(List!(Permission) permissions, AuthorizationInfo info) { 529 bool[] result; 530 if (permissions !is null && !permissions.isEmpty()) { 531 int size = permissions.size(); 532 result = new bool[size]; 533 int i = 0; 534 foreach (Permission p; permissions) { 535 result[i++] = isPermitted(p, info); 536 } 537 } else { 538 result = new bool[0]; 539 } 540 return result; 541 } 542 543 bool isPermittedAll(PrincipalCollection subjectIdentifier, string[] permissions...) { 544 if (permissions !is null && permissions.length > 0) { 545 Collection!(Permission) perms = new ArrayList!(Permission)(cast(int) permissions.length); 546 foreach (string permString; permissions) { 547 perms.add(getPermissionResolver().resolvePermission(permString)); 548 } 549 return isPermittedAll(subjectIdentifier, perms); 550 } 551 return false; 552 } 553 554 bool isPermittedAll(PrincipalCollection principal, Collection!(Permission) permissions) { 555 AuthorizationInfo info = getAuthorizationInfo(principal); 556 return info !is null && isPermittedAll(permissions, info); 557 } 558 559 protected bool isPermittedAll(Collection!(Permission) permissions, AuthorizationInfo info) { 560 if (permissions !is null && !permissions.isEmpty()) { 561 foreach (Permission p; permissions) { 562 if (!isPermitted(p, info)) { 563 return false; 564 } 565 } 566 } 567 return true; 568 } 569 570 void checkPermission(PrincipalCollection subjectIdentifier, string permission) { 571 Permission p = getPermissionResolver().resolvePermission(permission); 572 checkPermission(subjectIdentifier, p); 573 } 574 575 void checkPermission(PrincipalCollection principal, Permission permission) { 576 AuthorizationInfo info = getAuthorizationInfo(principal); 577 checkPermission(permission, info); 578 } 579 580 protected void checkPermission(Permission permission, AuthorizationInfo info) { 581 if (!isPermitted(permission, info)) { 582 string msg = "User is not permitted [" ~ (cast(Object) permission).toString() ~ "]"; 583 throw new UnauthorizedException(msg); 584 } 585 } 586 587 void checkPermissions(PrincipalCollection subjectIdentifier, string[] permissions...) { 588 if (permissions !is null) { 589 foreach (string permString; permissions) { 590 checkPermission(subjectIdentifier, permString); 591 } 592 } 593 } 594 595 void checkPermissions(PrincipalCollection principal, Collection!(Permission) permissions) { 596 AuthorizationInfo info = getAuthorizationInfo(principal); 597 checkPermissions(permissions, info); 598 } 599 600 protected void checkPermissions(Collection!(Permission) permissions, AuthorizationInfo info) { 601 if (permissions !is null && !permissions.isEmpty()) { 602 foreach (Permission p; permissions) { 603 checkPermission(p, info); 604 } 605 } 606 } 607 608 bool hasRole(PrincipalCollection principal, string roleIdentifier) { 609 AuthorizationInfo info = getAuthorizationInfo(principal); 610 return hasRole(roleIdentifier, info); 611 } 612 613 protected bool hasRole(string roleIdentifier, AuthorizationInfo info) { 614 // return info !is null && info.getRoles() !is null && info.getRoles().contains(roleIdentifier); 615 616 version (HUNT_SHIRO_DEBUG) 617 tracef("checking: %s", roleIdentifier); 618 if (info !is null) { 619 Collection!(string) roles = info.getRoles(); 620 if (roles !is null) { 621 bool r = roles.contains(roleIdentifier); 622 version (HUNT_SHIRO_DEBUG) { 623 infof("roles: %s, result: %s", roles, r); 624 } 625 return r; 626 } 627 } 628 return false; 629 } 630 631 bool[] hasRoles(PrincipalCollection principal, List!(string) roleIdentifiers) { 632 AuthorizationInfo info = getAuthorizationInfo(principal); 633 if (roleIdentifiers is null || roleIdentifiers.isEmpty()) { 634 return new bool[0]; 635 } 636 637 return hasRoles(roleIdentifiers.toArray(), info); 638 } 639 640 bool[] hasRoles(PrincipalCollection principal, string[] roleIdentifiers) { 641 AuthorizationInfo info = getAuthorizationInfo(principal); 642 bool[] result = new bool[roleIdentifiers.length]; 643 if (info !is null) { 644 result = hasRoles(roleIdentifiers, info); 645 } 646 return result; 647 } 648 649 // protected bool[] hasRoles(List!(string) roleIdentifiers, AuthorizationInfo info) { 650 // bool[] result; 651 // if (roleIdentifiers is null || roleIdentifiers.isEmpty()) { 652 // result = new bool[0]; 653 // } else { 654 // result = hasRoles(roleIdentifiers.toArray(), info); 655 // } 656 // return result; 657 // } 658 659 protected bool[] hasRoles(string[] roleIdentifiers, AuthorizationInfo info) { 660 bool[] result; 661 if (roleIdentifiers.empty()) { 662 result = new bool[0]; 663 } else { 664 result = new bool[roleIdentifiers.length]; 665 int i = 0; 666 foreach (string roleName; roleIdentifiers) { 667 result[i++] = hasRole(roleName, info); 668 } 669 } 670 return result; 671 } 672 673 bool hasAllRoles(PrincipalCollection principal, Collection!(string) roleIdentifiers) { 674 AuthorizationInfo info = getAuthorizationInfo(principal); 675 return info !is null && hasAllRoles(roleIdentifiers, info); 676 } 677 678 bool hasAllRoles(PrincipalCollection principal, string[] roleIdentifiers) { 679 AuthorizationInfo info = getAuthorizationInfo(principal); 680 return info !is null && hasAllRoles(roleIdentifiers, info); 681 } 682 683 private bool hasAllRoles(Collection!(string) roleIdentifiers, AuthorizationInfo info) { 684 if (roleIdentifiers !is null && !roleIdentifiers.isEmpty()) { 685 return hasAllRoles(roleIdentifiers.toArray(), info); 686 } 687 return true; 688 } 689 690 private bool hasAllRoles(string[] roleIdentifiers, AuthorizationInfo info) { 691 foreach (string roleName; roleIdentifiers) { 692 if (!hasRole(roleName, info)) { 693 return false; 694 } 695 } 696 return true; 697 } 698 699 void checkRole(PrincipalCollection principal, string role) { 700 AuthorizationInfo info = getAuthorizationInfo(principal); 701 checkRole(role, info); 702 } 703 704 protected void checkRole(string role, AuthorizationInfo info) { 705 if (!hasRole(role, info)) { 706 string msg = "User does not have role [" ~ role ~ "]"; 707 throw new UnauthorizedException(msg); 708 } 709 } 710 711 void checkRoles(PrincipalCollection principal, Collection!(string) roles) { 712 AuthorizationInfo info = getAuthorizationInfo(principal); 713 checkRoles(roles, info); 714 } 715 716 void checkRoles(PrincipalCollection principal, string[] roles...) { 717 718 AuthorizationInfo info = getAuthorizationInfo(principal); 719 if (!roles.empty()) { 720 foreach (string roleName; roles) { 721 checkRole(roleName, info); 722 } 723 } 724 } 725 726 protected void checkRoles(Collection!(string) roles, AuthorizationInfo info) { 727 if (roles !is null && !roles.isEmpty()) { 728 foreach (string roleName; roles) { 729 checkRole(roleName, info); 730 } 731 } 732 } 733 734 /** 735 * Calls {@code super.doClearCache} to ensure any cached authentication data is removed and then calls 736 * {@link #clearCachedAuthorizationInfo(hunt.shiro.subject.PrincipalCollection)} to remove any cached 737 * authorization data. 738 * <p/> 739 * If overriding in a subclass, be sure to call {@code super.doClearCache} to ensure this behavior is maintained. 740 * 741 * @param principals the principals of the account for which to clear any cached AuthorizationInfo 742 */ 743 override protected void doClearCache(PrincipalCollection principals) { 744 super.doClearCache(principals); 745 clearCachedAuthorizationInfo(principals); 746 } 747 }