001package com.randomnoun.common.security; 002 003/* (c) 2013 randomnoun. All Rights Reserved. This work is licensed under a 004 * BSD Simplified License. (http://www.randomnoun.com/bsd-simplified.html) 005 */ 006 007import java.io.IOException; 008import java.util.ArrayList; 009import java.util.HashMap; 010import java.util.Iterator; 011import java.util.List; 012import java.util.Map; 013 014import com.randomnoun.common.MRUCache; 015 016/** 017 * This class manages users, roles, resources and permissions for an application. 018 * 019 * <p>Most of the code that adds/deletes/maintains these objects has been removed, that is 020 * now the responsibility of the security implementation code. 021 * 022 * <p>Methods to read user and permission data are delegated to the SecurityLoader, and 023 * methods that authenticate users are delegated to the SecurityAuthenticator. 024 * 025 * <p>This class now mostly acts as a cache for user and role data, and can perform 026 * simple and complex permission checks for users against resources. 027 * 028 * <p>The following properties can be passed to the SecurityContext during construction; 029 * property keys are defined as static public final Strings in this class. 030 * 031 * <ul> 032 * <li>INIT_CASE_INSENSITIVE - make the security cache case-insensitive, typically when interfacing with 033 * Active Directory. Defaults to false. 034 * <li>INIT_USER_CACHE_SIZE - maximum size of user cache 035 * <li>INIT_USER_CACHE_EXPIRY - expiry time of users from the user cache (in milliseconds). If this 036 * property is not set, user caching is disabled. 037 * </ul> 038 * 039 * <p>Additional properties may also be required based on the SecurityLoader implementation used. 040 * 041 * @author knoxg 042 * 043 * @see com.randomnoun.common.security.SecurityLoader 044 */ 045public class SecurityContext { 046 047 // /** The logger for this class */ 048 // private static Logger logger = Logger.getLogger(SecurityContext.class.getName()); 049 050 /** SecurityContext properties */ 051 private Map<String, Object> properties = null; 052 053 /** An initialisation property key. See the class documentation for details. */ 054 public static final String INIT_USER_CACHE_SIZE = "securityContext.userCacheSize"; 055 056 /** An initialisation property key. See the class documentation for details. */ 057 public static final String INIT_USER_CACHE_EXPIRY = "securityContext.userCacheExpiry"; 058 059 /** An initialisation property key. See the class documentation for details. */ 060 public static final String INIT_USERNAME_MASK = "securityContext.usernameMask"; 061 062 /** An initialisation property key. See the class documentation for details. */ 063 public static final String INIT_CASE_INSENSITIVE = "securityContext.caseInsensitive"; 064 065 /** Maps rolenames to maps of permission names (in the form 'activity.resource') 066 * to Permission objects (possibly containing ResourceCriteria objects). 067 * 068 * If the security context is case-insensitive, then role names are lower-cased. 069 */ 070 private Map<String, Map<String, Permission>> rolePermissionCache = null; 071 072 /** Maps usernames to maps of permission names (in the form 'activity.resource') 073 * to Permission objects (possibly containing ResourceCriteria objects). 074 * 075 * If the security context is case-insensitive, then usernames are lower-cased. */ 076 private Map<User, Map<String, Permission>> userPermissionCache = null; 077 078 /** Maps user objects to list of roles. 079 * 080 * @TODO convert to HashSet ? 081 */ 082 private Map<User, List<String>> userRoleCache = null; 083 084 /** Maps userIds to Users. 085 */ 086 private Map<Long, User> userCache = null; 087 088 /** This security loader is used to retrieve information from a persistant data 089 * store for this context */ 090 private SecurityLoader securityLoader = null; 091 092 093 /** The authenticator, if you want to actually check passwords */ 094 private SecurityAuthenticator securityAuthenticator = null; 095 096 // should probably use guava caches for all of this now 097 098 /** This class is invoked by the MRUCache to recalculate values in the 099 * user permission cache that have expired, or where not in the cache to begin with. 100 */ 101 private static class UserPermissionCallback 102 implements MRUCache.RetrievalCallback { 103 104 SecurityLoader loader; 105 106 /** Creates a new callback used to populate the 107 * user cache 108 */ 109 public UserPermissionCallback(SecurityLoader loader) { 110 this.loader = loader; 111 } 112 113 /** this method is only called if the required value is not in the cache, 114 * or the value in the cache has expired. 115 * 116 * @param key The username of the User to return 117 */ 118 public Object get(Object key) { 119 if (key==null) { throw new NullPointerException("null key"); } 120 if (!(key instanceof User)) { 121 throw new IllegalArgumentException("Expected user as User, found " + key.getClass().getName()); 122 } 123 124 User user = (User) key; 125 126 Map<String, Permission> result = new HashMap<String, Permission>(); 127 try { 128 List<Permission> permissions = loader.loadUserPermissions(user); 129 for (Iterator<Permission> i = permissions.iterator(); i.hasNext(); ) { 130 Permission perm = (Permission) i.next(); 131 if (result.containsKey(perm.getActivity() + "." + perm.getResource())) { 132 throw new IllegalStateException("User '" + key + "' contains two versions of permission '" + 133 perm.getActivity() + "." + perm.getResource() + "'; please check the database"); 134 } 135 result.put(perm.getActivity() + "." + perm.getResource(), perm); 136 } 137 } catch (IOException ioe) { 138 throw new RuntimeException("IOException reading user permissions", ioe); 139 } 140 return result; 141 } 142 } 143 144 /** This class is invoked by the MRUCache to recalculate values in the 145 * user permission cache that have expired, or where not in the cache to begin with. 146 */ 147 private static class RolePermissionCallback 148 implements MRUCache.RetrievalCallback { 149 150 SecurityLoader loader; 151 152 /** Creates a new rowcount callback used to populate the 153 * user cache 154 */ 155 public RolePermissionCallback(SecurityLoader loader) { 156 this.loader = loader; 157 } 158 159 /** this method is only called if the required value is not in the cache, 160 * or the value in the cache has expired. 161 * 162 * @param key The username of the User to return 163 */ 164 public Object get(Object key) { 165 if (key==null) { throw new NullPointerException("null key"); } 166 if (!(key instanceof String)) { 167 throw new IllegalArgumentException("Expected roleName as string, found " + key.getClass().getName()); 168 } 169 String rolename = (String) key; 170 Map<String, Permission> result = new HashMap<String, Permission>(); 171 try { 172 List<Permission> permissions = loader.loadRolePermissions(rolename); 173 for (Iterator<Permission> i = permissions.iterator(); i.hasNext(); ) { 174 Permission perm = (Permission) i.next(); 175 if (result.containsKey(perm.getActivity() + "." + perm.getResource())) { 176 throw new IllegalStateException("Role '" + key + "' contains two versions of permission '" + 177 perm.getActivity() + "." + perm.getResource() + "'; please check the database"); 178 } 179 result.put(perm.getActivity() + "." + perm.getResource(), perm); 180 } 181 } catch (IOException ioe) { 182 throw new RuntimeException("IOException reading role permissions", ioe); 183 } 184 return result; 185 } 186 } 187 188 189 /** This class is invoked by the MRUCache to recalculate values in the 190 * user role cache that have expired, or where not in the cache to begin with. 191 */ 192 private static class UserRoleCallback 193 implements MRUCache.RetrievalCallback { 194 195 SecurityLoader loader; 196 197 /** Creates a new rowcount callback used to populate the 198 * user cache 199 */ 200 public UserRoleCallback(SecurityLoader loader) { 201 this.loader = loader; 202 } 203 204 /** this method is only called if the required value is not in the cache, 205 * or the value in the cache has expired. 206 * 207 * @param key The username of the User to return 208 */ 209 public Object get(Object key) { 210 if (key==null) { throw new NullPointerException("null key"); } 211 if (!(key instanceof User)) { 212 throw new IllegalArgumentException("Expected user as User, found " + key.getClass().getName()); 213 } 214 215 try { 216 // @TODO convert to HashSet ? 217 return loader.loadUserRoles((User) key); 218 } catch (IOException ioe) { 219 throw new RuntimeException("IOException reading user permissions", ioe); 220 } 221 } 222 } 223 224 225 /** This class is invoked by the MRUCache to load Users. It delegates to the Loader. 226 */ 227 private static class UserCallback 228 implements MRUCache.RetrievalCallback { 229 230 SecurityLoader loader; 231 232 /** Creates a new rowcount callback used to populate the 233 * user cache 234 */ 235 public UserCallback(SecurityLoader loader) { 236 this.loader = loader; 237 } 238 239 /** this method is only called if the required value is not in the cache, 240 * or the value in the cache has expired. 241 * 242 * @param key The username of the User to return 243 */ 244 public Object get(Object key) { 245 if (key==null) { throw new NullPointerException("null key"); } 246 if (!(key instanceof Number)) { 247 throw new IllegalArgumentException("Expected numeric userId, found " + key.getClass().getName()); 248 } 249 250 try { 251 // @TODO convert to HashSet ? 252 return loader.loadUser(((Number) key).longValue()); 253 } catch (IOException ioe) { 254 throw new RuntimeException("IOException reading user", ioe); 255 } 256 } 257 } 258 259 260 /** 261 * Creates a new SecurityContext object. 262 * 263 * @param properties Initialisation properties for this SecurityContext, its 264 * SecurityLoader, and SecurityAuthenticator 265 * 266 * @throws IllegalStateException if the context is configured to preload, 267 * and it fails to do so. 268 */ 269 public SecurityContext(Map<String, Object> properties, SecurityLoader securityLoader, 270 SecurityAuthenticator securityAuthenticator) { 271 this.properties = properties; 272 this.securityLoader = securityLoader; 273 this.securityLoader.initialise(properties); 274 this.securityAuthenticator = securityAuthenticator; 275 this.securityAuthenticator.initialise(properties); 276 resetSecurityContext(); 277 } 278 279 280 /** Retrieve a list of permissions for this user, as Permission objects. 281 * 282 * @param user 283 * @return 284 * @throws IOException 285 */ 286 public List<Permission> getUserPermissions(User user) throws IOException { 287 // @TODO should get this User out of our userCache, keyed by id 288 289 List<Permission> permissions = new ArrayList<Permission>(); 290 Map<String, Permission> cachedPermissions = userPermissionCache.get(user); 291 for (Iterator<Permission> i = cachedPermissions.values().iterator(); i.hasNext(); ) { 292 permissions.add(i.next()); 293 } 294 return permissions; 295 } 296 297 298 /** Returns a list of Permission objects that apply to the specified rolename. 299 * 300 * @param roleName the role name 301 * @return A List of Permission objects that apply to that role 302 */ 303 public List<Permission> getRolePermissions(String roleName) { 304 // retrieve from cache 305 List<Permission> result = new ArrayList<Permission>(); 306 if (roleName==null) { 307 throw new NullPointerException("null roleName"); 308 } else { 309 Map<String, Permission> rolePermissions = rolePermissionCache.get(roleName); 310 if (rolePermissions == null) { 311 throw new IllegalArgumentException("Unknown role '" + roleName + "'"); 312 } 313 for (Iterator<Permission> i = rolePermissions.values().iterator(); i.hasNext(); ) { 314 result.add(i.next()); 315 } 316 } 317 return result; 318 } 319 320 /** 321 * Return a list of User objects representing all users contained in this 322 * security context. Permission information relating to that user is not 323 * populated unless the 'populatePermission' parameter is set to true. 324 * 325 * <p>The information returned by this function may be cached, depending 326 * on the initialisation properties of the security context. 327 * 328 * @return A List of Users. 329 */ 330 public List<User> getAllUsers() throws IOException { 331 return securityLoader.loadAllUsers(); 332 } 333 334 /** 335 * Return a List of all resources in this security context, identified 336 * by String. 337 * 338 * <p>The information returned by this function may be cached, depending 339 * on the initialisation properties of the security context. 340 * 341 * @return A List of resources 342 */ 343 public List<String> getAllResources() throws IOException { 344 return securityLoader.loadAllResources(); 345 } 346 347 /** 348 * Return a List of all Permissions in this security context. 349 * 350 * <p>The information returned by this function may be cached, depending 351 * on the initialisation properties of the security context. 352 * 353 * @return A List of resources 354 */ 355 public List<Permission> getAllPermissions() throws IOException { 356 return securityLoader.loadAllPermissions(); 357 } 358 359 360 /** 361 * Return a List of all activities in this security context for a given 362 * resource, identified by String. 363 * 364 * <p>The information returned by this function may be cached, depending 365 * on the initialisation properties of the security context. 366 * 367 * @param resourceName The resource we wish to retrieve activities for 368 * 369 * @return A List of activities. 370 * 371 * @throws SecurityException 372 */ 373 public List<String> getAllActivities(String resourceName) throws IOException { 374 return securityLoader.loadAllActivities(resourceName); 375 } 376 377 /** 378 * Return a List of roles in this security context for the User, identified 379 * by String. 380 * 381 * <p>The information returned by this function may be cached, depending 382 * on the initialisation properties of the security context. 383 * 384 * @return A List of roles. 385 */ 386 public List<String> getAllRoles() 387 throws IOException { 388 return securityLoader.loadAllRoles(); 389 } 390 391 /** 392 * Return a List of all roles in this security context, identified 393 * by String. 394 * 395 * <p>The information returned by this function may be cached, depending 396 * on the initialisation properties of the security context. 397 * 398 * @return A List of roles. 399 */ 400 public List<String> getUserRoles(User user) 401 throws IOException 402 { 403 List<String> cachedRoles = userRoleCache.get(user); 404 if (cachedRoles==null) { 405 throw new IllegalStateException("Unknown user '" + user + "'"); 406 } 407 return cachedRoles; 408 } 409 410 /** 411 * Returns a detailed list of roles from the security context. Each role 412 * is defined as a Map with the following keys: 413 * 414 * <attributes> 415 * roleId - the numeric id for the role 416 * roleName - the name of the role for 417 * system - (Number) set to 1 if this role is read-only, 0 otherwise 418 * description - a description for the role 419 * </attributes> 420 * 421 * @return a list of roles, as described above 422 * 423 * @throws IOException 424 */ 425 public List<Map<String, Object>> getAllRoleDetails() 426 throws IOException { 427 return securityLoader.loadAllRoleDetails(); 428 } 429 430 /** 431 * Returns a detailed list of users from the security context. Each user 432 * is defined as a Map with the following keys: 433 * 434 * <attributes> 435 * userId - the login name for the user 436 * name - the full name of the user 437 * system - (Number) set to 1 if this role is read-only, 0 otherwise 438 * </attributes> 439 * 440 * @return a list of users, as described above 441 * 442 * @throws IOException 443 */ 444 public List<Map<String, Object>> getAllUserDetails() 445 throws IOException { 446 return securityLoader.loadAllUserDetails(); 447 } 448 449 450 451 452 /** Returns true if a user is allowed to perform the permission supplied. The permission 453 * is expressed in 'activity.resourceType' format, e.g. 'update.message'. No expression 454 * context is supplied; this method will not evaluate any conditional resource 455 * restrictions. This is useful in cases where the full resource context is not known, 456 * for example when a message is first created by a user. 457 * 458 * <p>In this case, the 'create.message' permission can be checked using this method 459 * before the user starts entering information, and 'create.message' can be 460 * checked with an expression context after the header fields have been populated. 461 * 462 * <p>If a permission is supplied that is not known by the application, this 463 * method will return false. 464 * 465 * @param user The user we are determining 466 * @param permission The permission we are testing for. Permissions are expressed in 467 * 'activity.resourceType' format. 468 * @return true if the permission is allowed, false is the permission is denied. 469 * 470 * @throws NullPointerException if either parameter to this method is null 471 * @throws IllegalArgumentException if the permission supplied is formatted incorrectly. 472 */ 473 public boolean hasPermission(User user, String permission) { 474 return hasPermission(user, permission, null); 475 } 476 477 /** 478 * Returns true if a user is allowed to perform the permission supplied, with 479 * given resource context. If a permission is assigned to both the user 480 * and the role, then the user permission is evaluated first. 481 * 482 * @param user The user we are determining 483 * @param permission The permission we are testing for. Permissions are expressed in 484 * 'activity.resourceType' format. 485 * @param context The resource context used to evaluate against the resource expression 486 * 487 * @return true if the permission is allowed, false is the permission is denied. 488 * 489 * @throws NullPointerException if either parameter to this method is null 490 * @throws IllegalArgumentException if the permission supplied is formatted incorrectly. 491 */ 492 public boolean hasPermission(User user, String permission, Map<String, Object> context) { 493 // @TODO should get this User out of our userCache, keyed by id 494 if (permission == null) { throw new NullPointerException("Null permission"); } 495 if (user == null) { throw new NullPointerException("Null user"); } 496 497 int pos = permission.indexOf('.'); 498 if (pos == -1) { 499 throw new IllegalArgumentException("Illegal permission value '" + permission + "'"); 500 } 501 502 // try per-user permissions... 503 Map<String, Permission> userPermissions = userPermissionCache.get(user); 504 if (userPermissions == null) { 505 // @TODO - load from DB ? should be handled by MRUCache 506 throw new IllegalStateException("Unknown user '" + user.getUsername() + "'"); 507 } else { 508 Permission userPermission = (Permission) userPermissions.get(permission); 509 if (userPermission!=null) { 510 if (context == null) { 511 return true; 512 } else { 513 ResourceCriteria criteria = userPermission.getResourceCriteria(); 514 if (criteria == null || criteria.evaluate(context)) { 515 return true; 516 } 517 } 518 } 519 } 520 521 // then try per-role permissions ... 522 List<String> roles = userRoleCache.get(user); 523 if (roles == null) { 524 // @TODO - load from DB ? should be handled by MRUCache 525 throw new IllegalStateException("Unknown user '" + user.getUsername() + "'"); 526 } else { 527 for (Iterator<String> i = roles.iterator(); i.hasNext(); ) { 528 String rolename = (String) i.next(); 529 Map<String, Permission> rolePermissions = rolePermissionCache.get(rolename); 530 if (rolePermissions == null) { 531 // @TODO - load from DB ? should be handled by MRUCache 532 throw new IllegalStateException("Unknown role '" + rolename + "'"); 533 } else { 534 // this is a strange data structure 535 Permission rolePermission = (Permission) rolePermissions.get(permission); 536 if (rolePermission!=null) { 537 if (context == null) { 538 return true; 539 } else { 540 ResourceCriteria criteria = rolePermission.getResourceCriteria(); 541 if (criteria == null || criteria.evaluate(context)) { 542 return true; 543 } 544 } 545 } 546 } 547 } 548 } 549 550 return false; 551 } 552 553 /** 554 * Returns the Permission object for a specific user/permission combination, or null 555 * if this permission is not granted. This method will not search the user's 556 * role-based permissions. 557 * 558 * @param user The user we are determining 559 * @param permission The permission we are testing for. Permissions are expressed in 560 * 'activity.resourceType' format. 561 * 562 * @return a permission object. 563 * 564 * @throws NullPointerException if either parameter to this method is null 565 * @throws IllegalArgumentException if the permission supplied is formatted incorrectly. 566 */ 567 public Permission getPermission(User user, String permission) { 568 // @TODO should get this User out of our userCache, keyed by id 569 if (permission == null) { throw new NullPointerException("Null permission"); } 570 if (user == null) { throw new NullPointerException("Null user"); } 571 572 int pos = permission.indexOf('.'); 573 if (pos == -1) { 574 throw new IllegalArgumentException("Illegal permission value '" + permission + "'"); 575 } 576 577 // try per-user permissions... 578 Map<String, Permission> userPermissions = userPermissionCache.get(user); 579 if (userPermissions == null) { 580 // @TODO - load from DB ? should be handled by MRUCache 581 throw new IllegalStateException("Unknown user '" + user.getUsername() + "'"); 582 } else { 583 Permission userPermission = (Permission) userPermissions.get(permission); 584 if (userPermission != null) { 585 return userPermission; 586 } 587 } 588 589 return null; 590 } 591 592 593 /** 594 * Returns a list of all Permission objects assigned to a user and all the 595 * roles that the user is a member of. This allows multiple permission conditions 596 * to be applied to a user, one for each role. 597 * 598 * @param user The user we are determining 599 * @param permission The permission we are testing for. Permissions are expressed in 600 * 'activity.resourceType' format. 601 * 602 * @return a List of Permission objects, or an empty list if the user 603 * (and none of their roles) contains this permission 604 * 605 * @throws NullPointerException if either parameter to this method is null 606 * @throws IllegalArgumentException if the permission supplied is formatted incorrectly. 607 */ 608 public List<Permission> getPermissions(User user, String permission) { 609 // @TODO should get this User out of our userCache, keyed by id 610 if (permission == null) { throw new NullPointerException("Null permission"); } 611 if (user == null) { throw new NullPointerException("Null user"); } 612 List<Permission> result = new ArrayList<Permission>(); 613 614 int pos = permission.indexOf('.'); 615 if (pos == -1) { 616 throw new IllegalArgumentException("Illegal permission value '" + permission + "'"); 617 } 618 619 // try per-user permissions... 620 Map<String, Permission> userPermissions = userPermissionCache.get(user); 621 if (userPermissions == null) { 622 // @TODO - load from DB ? should be handled by MRUCache 623 throw new IllegalStateException("Unknown user '" + user.getUsername() + "'"); 624 } else { 625 Permission userPermission = (Permission) userPermissions.get(permission); 626 if (userPermission != null) { 627 result.add(userPermission); 628 } 629 } 630 631 // then try per-role permissions ... 632 List<String> roles = userRoleCache.get(user); 633 if (roles == null) { 634 throw new IllegalStateException("Unknown user '" + user.getUsername() + "'"); 635 } else { 636 for (Iterator<String> i = roles.iterator(); i.hasNext(); ) { 637 String rolename = (String) i.next(); 638 Map<String, Permission> rolePermissions = rolePermissionCache.get(rolename); 639 if (rolePermissions == null) { 640 throw new IllegalStateException("Unknown role '" + rolename + "'"); 641 } else { 642 Permission rolePermission = (Permission) rolePermissions.get(permission); 643 if (rolePermission!=null) { 644 result.add(rolePermission); 645 } 646 } 647 } 648 } 649 return result; 650 } 651 652 653 654 /** Returns a string representation of this security context. 655 * 656 * @return a string representation of this security context. 657 */ 658 public String toString() { 659 return super.toString() + (rolePermissionCache == null ? " - uninitialised" : ": " + rolePermissionCache.toString()); 660 } 661 662 /** Clear all caches and re-initialises this security context (as defined 663 * in this instance's initial initialisation properties). 664 * This method also resets this security context's loader. 665 */ 666 @SuppressWarnings("unchecked") 667 public void resetSecurityContext() { 668 // logger.debug("Security context properties: " + properties.toString()); 669 670 int cacheSize = Integer.MAX_VALUE; 671 int cacheExpiry = Integer.MAX_VALUE; 672 if (properties.get(INIT_USER_CACHE_SIZE) != null && properties.get(INIT_USER_CACHE_EXPIRY) != null) { 673 cacheSize = Integer.parseInt((String) properties.get(INIT_USER_CACHE_SIZE)); 674 cacheExpiry = Integer.parseInt((String) properties.get(INIT_USER_CACHE_EXPIRY)); 675 } 676 677 UserPermissionCallback userPermissionCallback = new UserPermissionCallback(this.securityLoader); 678 userPermissionCache = new MRUCache(cacheSize, cacheExpiry, userPermissionCallback ); 679 680 UserRoleCallback userRoleCallback = new UserRoleCallback (this.securityLoader); 681 userRoleCache = new MRUCache(cacheSize, cacheExpiry, userRoleCallback ); 682 683 RolePermissionCallback rolePermissionCallback = new RolePermissionCallback (this.securityLoader); 684 rolePermissionCache = new MRUCache(cacheSize, cacheExpiry, rolePermissionCallback ); 685 686 UserCallback userCallback = new UserCallback (this.securityLoader); 687 userCache = new MRUCache(cacheSize, cacheExpiry, userCallback ); 688 689 try { 690 securityLoader.resetSecurityContext(); 691 } catch (IOException ioe) { 692 throw (IllegalArgumentException) new IllegalArgumentException( 693 "Cannot initialise security Context").initCause(ioe); 694 } 695 } 696 697 /** 698 * Authenticate the supplied username and password with the authentication provider. 699 * Returns true if the username/password combination is valid, false otherwise 700 * 701 * <p>Some authentication providers may require more complex handshakes (e.g. TFA authentication) 702 * which are currently suported by setting flags in a subclassed User object. 703 * Possible mangling the password parameter as well. See the securityAuthenticator 704 * documentation for details. 705 * 706 * <p>The User object passed to this method may not have a valid userId assigned to it (this 707 * may be set by the authentication provider). 708 * 709 * @param user user 710 * @param password password 711 * 712 * @return true if the username/password combination is valid, false otherwise 713 * 714 * @throws IOException an exception occurred accessing the authentication provider. 715 */ 716 public boolean authenticate(User user, String password) 717 throws IOException { 718 // @TODO should get this User out of our userCache, keyed by id 719 return securityAuthenticator.authenticate(user, password); 720 } 721 722 /** Returns a User, given their userId 723 * 724 * <p>This method will not load role or permissions data for the user. 725 * 726 * @param userId 727 * @return 728 */ 729 public User getUser(long userId) { 730 // this cache has different User objects than the User objects in the role/user permission caches, 731 User user = (User) userCache.get(userId); 732 return user; 733 } 734 735 736 // why are these methods public ? 737 public List<Permission> loadRolePermissions(String role) 738 throws IOException { 739 return securityLoader.loadRolePermissions(role); 740 } 741 742 public List<Permission> loadUserRolePermissions(User user) 743 throws IOException { 744 return securityLoader.loadUserRolePermissions(user); 745 } 746 747 748}