1 7 package org.jboss.security.plugins; 8 9 import java.lang.reflect.Method ; 10 import java.lang.reflect.UndeclaredThrowableException ; 11 import java.security.Principal ; 12 import java.security.acl.Group ; 13 import java.util.Arrays ; 14 import java.util.Enumeration ; 15 import java.util.HashSet ; 16 import java.util.Iterator ; 17 import java.util.Set ; 18 import javax.security.auth.Subject ; 19 import javax.security.auth.callback.CallbackHandler ; 20 import javax.security.auth.login.LoginContext ; 21 import javax.security.auth.login.LoginException ; 22 23 import org.jboss.logging.Logger; 24 import org.jboss.security.AnybodyPrincipal; 25 import org.jboss.security.NobodyPrincipal; 26 import org.jboss.security.RealmMapping; 27 import org.jboss.security.SecurityAssociation; 28 import org.jboss.security.SubjectSecurityManager; 29 import org.jboss.security.auth.callback.SecurityAssociationHandler; 30 import org.jboss.system.ServiceMBeanSupport; 31 import org.jboss.util.CachePolicy; 32 import org.jboss.util.TimedCachePolicy; 33 34 48 public class JaasSecurityManager extends ServiceMBeanSupport 49 implements SubjectSecurityManager, RealmMapping 50 { 51 53 public static class DomainInfo implements TimedCachePolicy.TimedEntry 54 { 55 private static Logger log = Logger.getLogger(DomainInfo.class); 56 private static boolean trace = log.isTraceEnabled(); 57 private LoginContext loginCtx; 58 private Subject subject; 59 private Object credential; 60 private Principal callerPrincipal; 61 private long expirationTime; 62 63 private boolean needsDestroy; 64 65 private int activeUsers; 66 67 public DomainInfo(int lifetime) 68 { 69 expirationTime = 1000 * lifetime; 70 } 71 72 synchronized int acquire() 73 { 74 return activeUsers ++; 75 } 76 synchronized int release() 77 { 78 int users = activeUsers --; 79 if( needsDestroy == true && users == 0 ) 80 { 81 if( trace ) 82 log.trace("needsDestroy is true, doing logout"); 83 logout(); 84 } 85 return users; 86 } 87 synchronized void logout() 88 { 89 if( trace ) 90 log.trace("logout, subject="+subject+", this="+this); 91 try 92 { 93 if( loginCtx != null ) 94 loginCtx.logout(); 95 } 96 catch(Throwable e) 97 { 98 if( trace ) 99 log.trace("Cache entry logout failed", e); 100 } 101 } 102 103 public void init(long now) 104 { 105 expirationTime += now; 106 } 107 public boolean isCurrent(long now) 108 { 109 return expirationTime > now; 110 } 111 public boolean refresh() 112 { 113 return false; 114 } 115 118 public void destroy() 119 { 120 if( trace ) 121 { 122 log.trace("destroy, subject="+subject+", this="+this 123 +", activeUsers="+activeUsers); 124 } 125 126 synchronized( this ) 127 { 128 if( activeUsers == 0 ) 129 logout(); 130 else 131 { 132 if( trace ) 133 log.trace("destroy saw activeUsers="+activeUsers); 134 needsDestroy = true; 135 } 136 } 137 } 138 public Object getValue() 139 { 140 return this; 141 } 142 public String toString() 143 { 144 StringBuffer tmp = new StringBuffer (super.toString()); 145 tmp.append('['); 146 tmp.append(SubjectActions.toString(subject)); 147 tmp.append(",credential.class="); 148 if( credential != null ) 149 { 150 Class c = credential.getClass(); 151 tmp.append(c.getName()); 152 tmp.append('@'); 153 tmp.append(System.identityHashCode(c)); 154 } 155 else 156 { 157 tmp.append("null"); 158 } 159 tmp.append(",expirationTime="); 160 tmp.append(expirationTime); 161 tmp.append(']'); 162 163 return tmp.toString(); 164 } 165 } 166 167 170 private String securityDomain; 171 174 private CachePolicy domainCache; 175 176 private CallbackHandler handler; 177 178 private Method setSecurityInfo; 179 180 182 protected Logger log; 183 protected boolean trace; 184 185 188 public JaasSecurityManager() 189 { 190 this("other", new SecurityAssociationHandler()); 191 } 192 199 public JaasSecurityManager(String securityDomain, CallbackHandler handler) 200 { 201 this.securityDomain = securityDomain; 202 this.handler = handler; 203 String categoryName = getClass().getName()+'.'+securityDomain; 204 this.log = Logger.getLogger(categoryName); 205 this.trace = log.isTraceEnabled(); 206 207 Class [] sig = {Principal .class, Object .class}; 209 try 210 { 211 setSecurityInfo = handler.getClass().getMethod("setSecurityInfo", sig); 212 } 213 catch (Exception e) 214 { 215 String msg = "Failed to find setSecurityInfo(Princpal, Object) method in handler"; 216 throw new UndeclaredThrowableException (e, msg); 217 } 218 log.debug("CallbackHandler: "+handler); 219 } 220 221 226 public void setCachePolicy(CachePolicy domainCache) 227 { 228 this.domainCache = domainCache; 229 log.debug("CachePolicy set to: "+domainCache); 230 } 231 232 235 public void flushCache() 236 { 237 if( domainCache != null ) 238 domainCache.flush(); 239 } 240 241 244 public String getSecurityDomain() 245 { 246 return securityDomain; 247 } 248 249 254 public Subject getActiveSubject() 255 { 256 260 return SecurityAssociation.getSubject(); 261 } 262 263 269 public boolean isValid(Principal principal, Object credential) 270 { 271 return isValid(principal, credential, null); 272 } 273 274 286 public boolean isValid(Principal principal, Object credential, 287 Subject activeSubject) 288 { 289 DomainInfo cacheInfo = getCacheInfo(principal, true); 291 if( trace ) 292 log.trace("Begin isValid, principal:"+principal+", cache info: "+cacheInfo); 293 294 boolean isValid = false; 295 if( cacheInfo != null ) 296 { 297 isValid = validateCache(cacheInfo, credential, activeSubject); 298 if( cacheInfo != null ) 299 cacheInfo.release(); 300 } 301 if( isValid == false ) 302 isValid = authenticate(principal, credential, activeSubject); 303 if( trace ) 304 log.trace("End isValid, "+isValid); 305 return isValid; 306 } 307 308 316 public Principal getPrincipal(Principal principal) 317 { 318 Principal result = principal; 319 synchronized( domainCache ) 321 { 322 DomainInfo info = getCacheInfo(principal, false); 323 if( trace ) 324 log.trace("getPrincipal, cache info: "+info); 325 if( info != null ) 326 { 327 result = info.callerPrincipal; 328 if( result == null ) 330 result = principal; 331 info.release(); 332 } 333 } 334 335 return result; 336 } 337 338 355 public boolean doesUserHaveRole(Principal principal, Set rolePrincipals) 356 { 357 boolean hasRole = false; 358 Subject subject = SubjectActions.getActiveSubject(); 360 if( subject != null ) 361 { 362 if( trace ) 364 log.trace("doesUserHaveRole(Set), subject: "+subject); 365 366 Group roles = getSubjectRoles(subject); 367 if( trace ) 368 log.trace("roles="+roles); 369 if( roles != null ) 370 { 371 Iterator iter = rolePrincipals.iterator(); 372 while( hasRole == false && iter.hasNext() ) 373 { 374 Principal role = (Principal ) iter.next(); 375 hasRole = doesRoleGroupHaveRole(role, roles); 376 if( trace ) 377 log.trace("hasRole("+role+")="+hasRole); 378 } 379 } 380 if( trace ) 381 log.trace("hasRole="+hasRole); 382 } 383 return hasRole; 384 } 385 386 397 public boolean doesUserHaveRole(Principal principal, Principal role) 398 { 399 boolean hasRole = false; 400 Subject subject = SubjectActions.getActiveSubject(); 402 if( subject != null ) 403 { 404 if( trace ) 406 log.trace("doesUserHaveRole(Principal), subject: "+subject); 407 408 Group roles = getSubjectRoles(subject); 409 if( roles != null ) 410 { 411 hasRole = doesRoleGroupHaveRole(role, roles); 412 } 413 } 414 return hasRole; 415 } 416 417 425 public Set getUserRoles(Principal principal) 426 { 427 HashSet userRoles = null; 428 Subject subject = SubjectActions.getActiveSubject(); 430 if( subject != null ) 431 { 432 if( trace ) 434 log.trace("getUserRoles, subject: "+subject); 435 436 Group roles = getSubjectRoles(subject); 437 if( roles != null ) 438 { 439 userRoles = new HashSet (); 440 Enumeration members = roles.members(); 441 while( members.hasMoreElements() ) 442 { 443 Principal role = (Principal ) members.nextElement(); 444 userRoles.add(role); 445 } 446 } 447 } 448 return userRoles; 449 } 450 451 460 protected boolean doesRoleGroupHaveRole(Principal role, Group userRoles) 461 { 462 if (role instanceof NobodyPrincipal) 464 return false; 465 466 boolean isMember = userRoles.isMember(role); 468 if (isMember == false) 469 { isMember = (role instanceof AnybodyPrincipal); 471 } 472 473 return isMember; 474 } 475 476 483 private boolean authenticate(Principal principal, Object credential, 484 Subject theSubject) 485 { 486 Subject subject = null; 487 boolean authenticated = false; 488 LoginException authException = null; 489 490 try 491 { 492 LoginContext lc = defaultLogin(principal, credential); 494 subject = lc.getSubject(); 495 496 if( subject != null ) 498 { 499 if( theSubject != null ) 501 { 502 SubjectActions.copySubject(subject, theSubject); 503 } 504 else 505 { 506 theSubject = subject; 507 } 508 509 authenticated = true; 510 updateCache(lc, subject, principal, credential); 512 } 513 } 514 catch(LoginException e) 515 { 516 if( principal != null && principal.getName() != null || trace ) 518 log.trace("Login failure", e); 519 authException = e; 520 } 521 SubjectActions.setContextInfo("org.jboss.security.exception", authException); 523 524 return authenticated; 525 } 526 527 532 private LoginContext defaultLogin(Principal principal, Object credential) 533 throws LoginException 534 { 535 539 Object [] securityInfo = {principal, credential}; 540 CallbackHandler theHandler = null; 541 try 542 { 543 theHandler = (CallbackHandler ) handler.getClass().newInstance(); 544 setSecurityInfo.invoke(theHandler, securityInfo); 545 } 546 catch (Throwable e) 547 { 548 if( trace ) 549 log.trace("Failed to create/setSecurityInfo on handler", e); 550 LoginException le = new LoginException ("Failed to setSecurityInfo on handler"); 551 le.initCause(e); 552 throw le; 553 } 554 Subject subject = new Subject (); 555 LoginContext lc = null; 556 if( trace ) 557 log.trace("defaultLogin, principal="+principal); 558 lc = SubjectActions.createLoginContext(securityDomain, subject, theHandler); 559 lc.login(); 560 if( trace ) 561 log.trace("defaultLogin, lc="+lc+", subject="+SubjectActions.toString(subject)); 562 return lc; 563 } 564 565 567 private boolean validateCache(DomainInfo info, Object credential, 568 Subject theSubject) 569 { 570 if( trace ) 571 { 572 StringBuffer tmp = new StringBuffer ("Begin validateCache, info="); 573 tmp.append(info.toString()); 574 tmp.append(";credential.class="); 575 if( credential != null ) 576 { 577 Class c = credential.getClass(); 578 tmp.append(c.getName()); 579 tmp.append('@'); 580 tmp.append(System.identityHashCode(c)); 581 } 582 else 583 { 584 tmp.append("null"); 585 } 586 log.trace(tmp.toString()); 587 } 588 589 Object subjectCredential = info.credential; 590 boolean isValid = false; 591 if( credential == null || subjectCredential == null ) 593 { 594 isValid = (credential == null) && (subjectCredential == null); 596 } 597 else if( subjectCredential.getClass().isAssignableFrom(credential.getClass()) ) 599 { 600 603 if( subjectCredential instanceof Comparable ) 604 { 605 Comparable c = (Comparable ) subjectCredential; 606 isValid = c.compareTo(credential) == 0; 607 } 608 else if( subjectCredential instanceof char[] ) 609 { 610 char[] a1 = (char[]) subjectCredential; 611 char[] a2 = (char[]) credential; 612 isValid = Arrays.equals(a1, a2); 613 } 614 else if( subjectCredential instanceof byte[] ) 615 { 616 byte[] a1 = (byte[]) subjectCredential; 617 byte[] a2 = (byte[]) credential; 618 isValid = Arrays.equals(a1, a2); 619 } 620 else if( subjectCredential.getClass().isArray() ) 621 { 622 Object [] a1 = (Object []) subjectCredential; 623 Object [] a2 = (Object []) credential; 624 isValid = Arrays.equals(a1, a2); 625 } 626 else 627 { 628 isValid = subjectCredential.equals(credential); 629 } 630 } 631 632 if( isValid ) 634 { 635 if( theSubject != null ) 637 { 638 SubjectActions.copySubject(info.subject, theSubject); 639 } 640 } 641 if( trace ) 642 log.trace("End validateCache, isValid="+isValid); 643 644 return isValid; 645 } 646 647 659 private DomainInfo getCacheInfo(Principal principal, boolean allowRefresh) 660 { 661 if( domainCache == null ) 662 return null; 663 664 DomainInfo cacheInfo = null; 665 synchronized( domainCache ) 666 { 667 if( allowRefresh == true ) 668 cacheInfo = (DomainInfo) domainCache.get(principal); 669 else 670 cacheInfo = (DomainInfo) domainCache.peek(principal); 671 if( cacheInfo != null ) 672 cacheInfo.acquire(); 673 } 674 return cacheInfo; 675 } 676 677 private Subject updateCache(LoginContext lc, Subject subject, 678 Principal principal, Object credential) 679 { 680 if( domainCache == null ) 682 return subject; 683 684 int lifetime = 0; 685 if( domainCache instanceof TimedCachePolicy ) 686 { 687 TimedCachePolicy cache = (TimedCachePolicy) domainCache; 688 lifetime = cache.getDefaultLifetime(); 689 } 690 DomainInfo info = new DomainInfo(lifetime); 691 info.loginCtx = lc; 692 info.subject = new Subject (); 693 SubjectActions.copySubject(subject, info.subject, true); 694 info.credential = credential; 695 696 if( trace ) 697 { 698 log.trace("updateCache, inputSubject="+SubjectActions.toString(subject) 699 +", cacheSubject="+SubjectActions.toString(info.subject)); 700 } 701 702 705 Set subjectGroups = subject.getPrincipals(Group .class); 706 Iterator iter = subjectGroups.iterator(); 707 while( iter.hasNext() ) 708 { 709 Group grp = (Group ) iter.next(); 710 String name = grp.getName(); 711 if( name.equals("CallerPrincipal") ) 712 { 713 Enumeration members = grp.members(); 714 if( members.hasMoreElements() ) 715 info.callerPrincipal = (Principal ) members.nextElement(); 716 } 717 } 718 719 724 if( principal == null && info.callerPrincipal == null ) 725 { 726 Set subjectPrincipals = subject.getPrincipals(Principal .class); 727 iter = subjectPrincipals.iterator(); 728 while( iter.hasNext() ) 729 { 730 Principal p = (Principal ) iter.next(); 731 if( (p instanceof Group ) == false ) 732 info.callerPrincipal = p; 733 } 734 } 735 736 741 synchronized( domainCache ) 742 { 743 if( domainCache.peek(principal) != null ) 744 domainCache.remove(principal); 745 domainCache.insert(principal, info); 746 if( trace ) 747 log.trace("Inserted cache info: "+info); 748 } 749 return info.subject; 750 } 751 752 757 private Group getSubjectRoles(Subject theSubject) 758 { 759 Set subjectGroups = theSubject.getPrincipals(Group .class); 760 Iterator iter = subjectGroups.iterator(); 761 Group roles = null; 762 while( iter.hasNext() ) 763 { 764 Group grp = (Group ) iter.next(); 765 String name = grp.getName(); 766 if( name.equals("Roles") ) 767 roles = grp; 768 } 769 return roles; 770 } 771 } 772 | Popular Tags |