[jboss-cvs] JBossAS SVN: r57340 - branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sun Oct 1 21:14:03 EDT 2006


Author: ryan.campbell at jboss.com
Date: 2006-10-01 21:14:03 -0400 (Sun, 01 Oct 2006)
New Revision: 57340

Modified:
   branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins/JaasSecurityManager.java
   branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins/SubjectActions.java
Log:
ASPATCH-38: JBAS-3397: Slow performance of JaasSecurityManager

Modified: branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins/JaasSecurityManager.java
===================================================================
--- branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins/JaasSecurityManager.java	2006-10-02 01:01:57 UTC (rev 57339)
+++ branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins/JaasSecurityManager.java	2006-10-02 01:14:03 UTC (rev 57340)
@@ -1,5 +1,5 @@
 /*
- * JBoss, the OpenSource EJB server
+ * JBoss, Home of Professional Open Source 
  *
  * Distributable under LGPL license.
  * See terms of license at gnu.org.
@@ -37,7 +37,7 @@
  domain name associated with the class for authentication,
  and the context JAAS Subject object for role mapping.
  
- @see #isValid(Principal, Object)
+ @see #isValid(Principal, Object, Subject)
  @see #getPrincipal(Principal)
  @see #doesUserHaveRole(Principal, Set)
  
@@ -59,11 +59,47 @@
       private Object credential;
       private Principal callerPrincipal;
       private long expirationTime;
-
+      /** Is there an active authentication in process */ 
+      private boolean needsDestroy; 
+      /** The number of users sharing this DomainInfo */ 
+      private int activeUsers; 
+      
       public DomainInfo(int lifetime)
       {
          expirationTime = 1000 * lifetime;
       }
+      
+      synchronized int acquire() 
+      { 
+         return activeUsers ++; 
+      } 
+      synchronized int release() 
+      { 
+         int users = activeUsers --; 
+         if( needsDestroy == true && users == 0 ) 
+         { 
+            if( trace ) 
+               log.trace("needsDestroy is true, doing logout"); 
+            logout(); 
+         } 
+         return users; 
+      } 
+      synchronized void logout() 
+      { 
+         if( trace ) 
+            log.trace("logout, subject="+subject+", this="+this); 
+         try 
+         { 
+            if( loginCtx != null ) 
+               loginCtx.logout(); 
+         } 
+         catch(Throwable e) 
+         { 
+            if( trace ) 
+               log.trace("Cache entry logout failed", e); 
+         } 
+      }
+      
       public void init(long now)
       {
          expirationTime += now;
@@ -78,17 +114,20 @@
       }
       public void destroy()
       {
-         if( trace )
-            log.trace("destroy, subject="+subject+", this="+this);
-         try
-         {
-            loginCtx.logout();
-         }
-         catch(Exception e)
-         {
-            if( trace )
-               log.trace("Cache entry logout failed", e);
-         }
+    	  if( trace )
+    		  log.trace("destroy, subject="+subject+", this="+this 
+    				  +", activeUsers="+activeUsers); 
+    	  synchronized( this ) 
+    	  {
+    		  if( activeUsers == 0 ) 
+    			  logout(); 
+    		  else 
+    		  { 
+    			  if( trace ) 
+    				  log.trace("destroy saw activeUsers="+activeUsers); 
+    			  needsDestroy = true; 
+    		  }  
+    	  }
       }
       public Object getValue()
       {
@@ -98,10 +137,7 @@
       {
          StringBuffer tmp = new StringBuffer(super.toString());
          tmp.append('[');
-         tmp.append("Subject(");
-         tmp.append(System.identityHashCode(subject));
-         tmp.append(").principals=");
-         tmp.append(subject.getPrincipals());
+         tmp.append(SubjectActions.toString(subject)); 
          tmp.append(",credential.class=");
          if( credential != null )
          {
@@ -173,12 +209,13 @@
          String msg = "Failed to find setSecurityInfo(Princpal, Object) method in handler";
          throw new UndeclaredThrowableException(e, msg);
       }
+      log.debug("CallbackHandler: "+handler);
    }
 
    /** The domainCache is typically a shared object that is populated
     by the login code(LoginModule, etc.) and read by this class in the
     isValid() method.
-    @see #isValid(Principal, Object)
+    @see #isValid(Principal, Object, Subject)
     */
    public void setCachePolicy(CachePolicy domainCache)
    {
@@ -210,6 +247,10 @@
     */
    public Subject getActiveSubject()
    {
+	 /* This does not use SubjectActions.getActiveSubject since the caller 
+       must have the correct permissions to access the 
+       SecurityAssociation.getSubject method. 
+     */
       return SecurityAssociation.getSubject();
    }
 
@@ -236,17 +277,22 @@
       the state of the authenticated Subject.
     @return true if the principal was authenticated, false otherwise.
     */
-   public synchronized boolean isValid(Principal principal, Object credential,
+   public boolean isValid(Principal principal, Object credential,
       Subject activeSubject)
    {
       // Check the cache first
       DomainInfo cacheInfo = getCacheInfo(principal, true);
       if( trace )
-         log.trace("Begin isValid, cache info: "+cacheInfo);
+    	  log.trace("Begin isValid, principal:"+principal+", cache info: "+cacheInfo); 
 
       boolean isValid = false;
       if( cacheInfo != null )
-         isValid = validateCache(cacheInfo, credential, activeSubject);
+      {
+    	  isValid = validateCache(cacheInfo, credential, activeSubject); 
+    	  if( cacheInfo != null ) 
+              cacheInfo.release();
+      }
+         
       if( isValid == false )
          isValid = authenticate(principal, credential, activeSubject);
       if( trace )
@@ -277,6 +323,7 @@
             // If the mapping did not have a callerPrincipal just use principal
             if( result == null )
                result = principal;
+            info.release(); 
          }
       }
 
@@ -285,9 +332,13 @@
 
    /** Does the current Subject have a role(a Principal) that equates to one
     of the role names. This method obtains the Group named 'Roles' from
-    the principal set of the currently authenticated Subject and then
-    creates a SimplePrincipal for each name in roleNames. If the role is
-    a member of the Roles group, then the user has the role.
+    the principal set of the currently authenticated Subject as determined 
+    by the SecurityAssociation.getSubject() method and then creates a 
+    SimplePrincipal for each name in roleNames. If the role is a member of the 
+    Roles group, then the user has the role. This requires that the caller 
+    establish the correct SecurityAssociation subject prior to calling this 
+    method. In the past this was done as a side-effect of an isValid() call, 
+    but this is no longer the case. 
     @param principal - ignored. The current authenticated Subject determines
     the active user and assigned user roles.
     @param rolePrincipals - a Set of Principals for the roles to check.
@@ -316,6 +367,8 @@
             {
                Principal role = (Principal) iter.next();
                hasRole = doesRoleGroupHaveRole(role, roles);
+               if( trace ) 
+                   log.trace("hasRole("+role+")="+hasRole); 
             }
          }
          if( trace )
@@ -324,11 +377,16 @@
       return hasRole;
    }
 
-   /** Validates operational environment Principal against the specified
-    application domain role.
-    @param principal - the caller principal as known in the operation environment.
-    @param role - the application domain role that the principal is to be validated against.
-    @return true if the principal has the role, false otherwise.
+   /** Does the current Subject have a role(a Principal) that equates to one 
+   of the role names. 
+
+   @see #doesUserHaveRole(Principal, Set) 
+
+   @param principal - ignored. The current authenticated Subject determines 
+   the active user and assigned user roles. 
+   @param role - the application domain role that the principal is to be 
+     validated against. 
+   @return true if the active principal has the role, false otherwise. 
     */
    public boolean doesUserHaveRole(Principal principal, Principal role)
    {
@@ -350,9 +408,13 @@
       return hasRole;
    }
 
-   /** Return the set of domain roles the principal has been assigned.
-   @return The Set<Principal> for the application domain roles that the
-   principal has been assigned.
+   /** Return the set of domain roles the current active Subject 'Roles' group 
+       found in the subject Principals set. 
+
+       @param principal - ignored. The current authenticated Subject determines 
+       the active user and assigned user roles. 
+       @return The Set<Principal> for the application domain roles that the 
+       principal has been assigned. 
    */
    public Set getUserRoles(Principal principal)
    {
@@ -464,23 +526,34 @@
    private LoginContext defaultLogin(Principal principal, Object credential)
       throws LoginException
    {
-      // We use our internal CallbackHandler to provide the security info
+	  /* We use our internal CallbackHandler to provide the security info. A 
+	      copy must be made to ensure there is a unique handler per active 
+	      login since there can be multiple active logins. 
+	     */ 
       Object[] securityInfo = {principal, credential};
+      CallbackHandler theHandler = null; 
       try
       {
-         setSecurityInfo.invoke(handler, securityInfo);
+    	  theHandler = (CallbackHandler) handler.getClass().newInstance(); 
+          setSecurityInfo.invoke(theHandler, securityInfo); 
       }
-      catch (Exception e)
+      catch (Throwable  e)
       {
          if( trace )
             log.trace("Failed to setSecurityInfo on handler", e);
-         throw new LoginException("Failed to setSecurityInfo on handler, msg="
-            + e.getMessage());
+         log.trace("Failed to create/setSecurityInfo on handler", e); 
+         LoginException le = new LoginException("Failed to setSecurityInfo on handler"); 
+         le.initCause(e); 
+         throw le; 
       }
       Subject subject = new Subject();
       LoginContext lc = null;
-      lc = SubjectActions.createLoginContext(securityDomain, subject, handler);
+      if( trace ) 
+          log.trace("defaultLogin, principal="+principal); 
+      lc = SubjectActions.createLoginContext(securityDomain, subject, theHandler); 
       lc.login();
+      if( trace ) 
+          log.trace("defaultLogin, lc="+lc+", subject="+SubjectActions.toString(subject));
       return lc;
    }
 
@@ -569,10 +642,9 @@
    /** An accessor method that synchronizes access on the domainCache
     to avoid a race condition that can occur when the cache entry expires
     in the presence of multi-threaded access. The allowRefresh flag should
-    be true for authentication accesses and false for authorization accesses.
-    If it were to be true for an authorization access a previously authenticated
-    user could be seen to not have their expected permissions due to a cache
-    expiration.
+    be true for authentication accesses and false for other accesses. 
+    Previously the other accesses included authorization and caller principal 
+    mapping. Now the only use of the  
 
     @param principal - the caller identity whose cached credentials are to
     be accessed.
@@ -591,6 +663,8 @@
             cacheInfo = (DomainInfo) domainCache.get(principal);
           else
             cacheInfo = (DomainInfo) domainCache.peek(principal);
+          if( cacheInfo != null ) 
+              cacheInfo.acquire(); 
       }
       return cacheInfo;
    }
@@ -615,7 +689,10 @@
       info.credential = credential;
 
       if( trace )
-         log.trace("updateCache, subject="+subject);
+      { 
+          log.trace("updateCache, inputSubject="+SubjectActions.toString(subject) 
+             +", cacheSubject="+SubjectActions.toString(info.subject)); 
+       } 
 
      /* Get the Subject callerPrincipal by looking for a Group called
         'CallerPrincipal'

Modified: branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins/SubjectActions.java
===================================================================
--- branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins/SubjectActions.java	2006-10-02 01:01:57 UTC (rev 57339)
+++ branches/JBoss_4_0_2_CP/security/src/main/org/jboss/security/plugins/SubjectActions.java	2006-10-02 01:14:03 UTC (rev 57340)
@@ -27,6 +27,35 @@
  */
 class SubjectActions
 {
+	private static class ToStringSubjectAction implements PrivilegedAction 
+	   { 
+	      Subject subject; 
+	      ToStringSubjectAction(Subject subject) 
+	      { 
+	         this.subject = subject; 
+	      } 
+	      public Object run() 
+	      { 
+	         StringBuffer tmp = new StringBuffer(); 
+	         tmp.append("Subject("); 
+	         tmp.append(System.identityHashCode(subject)); 
+	         tmp.append(").principals="); 
+	         Iterator principals = subject.getPrincipals().iterator(); 
+	         while( principals.hasNext() ) 
+	         { 
+	            Object p = principals.next(); 
+	            Class c = p.getClass(); 
+	            tmp.append(c.getName()); 
+	            tmp.append('@'); 
+	            tmp.append(System.identityHashCode(c)); 
+	            tmp.append('('); 
+	            tmp.append(p); 
+	            tmp.append(')'); 
+	         } 
+	         return tmp.toString(); 
+	      } 
+	   } 
+	
    private static class GetSubjectAction implements PrivilegedAction
    {
       static PrivilegedAction ACTION = new GetSubjectAction();
@@ -176,7 +205,10 @@
    static void copySubject(Subject fromSubject, Subject toSubject, boolean setReadOnly)
    {
       CopySubjectAction action = new CopySubjectAction(fromSubject, toSubject, setReadOnly);
-      AccessController.doPrivileged(action);
+      if( System.getSecurityManager() != null ) 
+          AccessController.doPrivileged(action); 
+       else 
+          action.run(); 
    }
 
    static LoginContext createLoginContext(String securityDomain, Subject subject,
@@ -235,6 +267,13 @@
          PrincipalInfoAction.PRIVILEGED.pop();
       }
    }
+   
+   static String toString(Subject subject) 
+   { 
+      ToStringSubjectAction action = new ToStringSubjectAction(subject); 
+      String info = (String) AccessController.doPrivileged(action); 
+      return info; 
+   } 
 
 }
 




More information about the jboss-cvs-commits mailing list