[jboss-cvs] JBossAS SVN: r74502 - projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Thu Jun 12 16:08:28 EDT 2008


Author: mmoyses
Date: 2008-06-12 16:08:28 -0400 (Thu, 12 Jun 2008)
New Revision: 74502

Added:
   projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManager.java
Removed:
   projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManagerBase.java
Log:
SECURITY-237
renaming the class

Copied: projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManager.java (from rev 74501, projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManagerBase.java)
===================================================================
--- projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManager.java	                        (rev 0)
+++ projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManager.java	2008-06-12 20:08:28 UTC (rev 74502)
@@ -0,0 +1,615 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.security.plugins.auth;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.jboss.logging.Logger;
+import org.jboss.security.AuthenticationManager;
+import org.jboss.security.AuthorizationManager;
+import org.jboss.security.RealmMapping;
+import org.jboss.security.SecurityConstants;
+import org.jboss.security.SecurityContext;
+import org.jboss.security.SecurityContextAssociation;
+import org.jboss.security.SecurityUtil;
+import org.jboss.security.SubjectSecurityManager;
+import org.jboss.security.auth.callback.SecurityAssociationHandler;
+import org.jboss.security.plugins.auth.JaasSecurityManagerBase.DomainInfo;
+import org.jboss.util.CachePolicy;
+import org.jboss.util.TimedCachePolicy;
+
+/** The JaasSecurityManager is responsible both for authenticating credentials
+ associated with principals and for role mapping. This implementation relies
+ on the JAAS LoginContext/LoginModules associated with the security
+ domain name associated with the class for authentication,
+ and the context JAAS Subject object for role mapping.
+ 
+ @see #isValid(Principal, Object, Subject)
+ @see #getPrincipal(Principal)
+ @see #doesUserHaveRole(Principal, Set)
+ 
+ @author <a href="on at ibis.odessa.ua">Oleg Nitz</a>
+ @author Scott.Stark at jboss.org
+ @author Anil.Saldhana at jboss.org
+ @author <a href="mmoyses at redhat.com">Marcus Moyses</a>
+ @version $Revision: 62860 $
+*/
+public class SynchronizedJaasSecurityManager implements SubjectSecurityManager, RealmMapping
+{
+   /** The name of the domain this instance is securing. It is used as
+    the appName into the SecurityPolicy.
+    */
+   private String securityDomain;
+
+   /** A cache of DomainInfo objects keyd by Principal. This is now
+    always set externally by our security manager service.
+    */
+   private CachePolicy domainCache;
+
+   /** The JAAS callback handler to use in defaultLogin */
+   private CallbackHandler handler;
+
+   /** The setSecurityInfo(Principal, Object) method of the handler obj */
+   private transient Method setSecurityInfo;
+
+   /** The flag to indicate that the Subject sets need to be deep copied*/
+   private boolean deepCopySubjectOption = false;
+
+   /** The log4j category for the security manager domain
+    */
+   protected Logger log;
+
+   protected boolean trace;
+
+   /** Creates a default JaasSecurityManager for with a securityDomain
+    name of 'other'.
+    */
+   public SynchronizedJaasSecurityManager()
+   {
+      this("other", new SecurityAssociationHandler());
+   }
+
+   /** Creates a JaasSecurityManager for with a securityDomain
+    name of that given by the 'securityDomain' argument.
+    @param securityDomain the name of the security domain
+    @param handler the JAAS callback handler instance to use
+    @exception UndeclaredThrowableException thrown if handler does not
+      implement a setSecurityInfo(Princpal, Object) method
+    */
+   public SynchronizedJaasSecurityManager(String securityDomain, CallbackHandler handler)
+   {
+      this.securityDomain = securityDomain;
+      this.handler = handler;
+      String categoryName = getClass().getName() + '.' + securityDomain;
+      this.log = Logger.getLogger(categoryName);
+      this.trace = log.isTraceEnabled();
+
+      // Get the setSecurityInfo(Principal principal, Object credential) method
+      Class<?>[] sig =
+      {Principal.class, Object.class};
+      try
+      {
+         setSecurityInfo = handler.getClass().getMethod("setSecurityInfo", sig);
+      }
+      catch (Exception e)
+      {
+         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, Subject)
+    */
+   public void setCachePolicy(CachePolicy domainCache)
+   {
+      this.domainCache = domainCache;
+      log.debug("CachePolicy set to: " + domainCache);
+   }
+
+   /**
+    * Flag to specify if deep copy of subject sets needs to be 
+    * enabled
+    * 
+    * @param flag
+    */
+   public void setDeepCopySubjectOption(Boolean flag)
+   {
+      log.debug("setDeepCopySubjectOption=" + flag);
+      this.deepCopySubjectOption = (flag == Boolean.TRUE);
+   }
+
+   /** Not really used anymore as the security manager service manages the
+    security domain authentication caches.
+    */
+   public void flushCache()
+   {
+      if (domainCache != null)
+         domainCache.flush();
+   }
+
+   /** Get the name of the security domain associated with this security mgr.
+    @return Name of the security manager security domain.
+    */
+   public String getSecurityDomain()
+   {
+      return securityDomain;
+   }
+
+   /** Get the currently authenticated Subject. This is a thread local
+    property shared across all JaasSecurityManager instances.
+    @return The Subject authenticated in the current thread if one
+    exists, null otherwise.
+    */
+   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();
+      Subject subj = null;
+      SecurityContext sc = SecurityContextAssociation.getSecurityContext();
+      if (sc != null)
+      {
+         subj = sc.getUtil().getSubject();
+      }
+      return subj;
+   }
+
+   /** Validate that the given credential is correct for principal. This
+    returns the value from invoking isValid(principal, credential, null).
+    @param principal - the security domain principal attempting access
+    @param credential - the proof of identity offered by the principal
+    @return true if the principal was authenticated, false otherwise.
+    */
+   public boolean isValid(Principal principal, Object credential)
+   {
+      return isValid(principal, credential, null);
+   }
+
+   /** Validate that the given credential is correct for principal. This first
+    will check the current CachePolicy object if one exists to see if the
+    user's cached credentials match the given credential. If there is no
+    credential cache or the cache information is invalid or does not match,
+    the user is authenticated against the JAAS login modules configured for
+    the security domain.
+    @param principal - the security domain principal attempting access
+    @param credential  the proof of identity offered by the principal
+    @param activeSubject - if not null, a Subject that will be populated with
+      the state of the authenticated Subject.
+    @return true if the principal was authenticated, false otherwise.
+    */
+   public boolean isValid(Principal principal, Object credential, Subject activeSubject)
+   {
+      // Check the cache first
+      DomainInfo cacheInfo = getCacheInfo(principal, true);
+      if (trace)
+         log.trace("Begin isValid, principal:" + principal + ", cache info: " + cacheInfo);
+
+      boolean isValid = false;
+      if (principal != null && principal.getName() != null)
+      {
+         synchronized (principal.getName().intern())
+         {
+            if (cacheInfo != null)
+            {
+               isValid = validateCache(cacheInfo, credential, activeSubject);
+               cacheInfo.release();
+            }
+            if (isValid == false)
+               isValid = authenticate(principal, credential, activeSubject);
+         }
+      }
+      else
+      {
+         if (cacheInfo != null)
+            cacheInfo.release();
+         isValid = authenticate(principal, credential, activeSubject);
+      }
+      if (trace)
+         log.trace("End isValid, " + isValid);
+      return isValid;
+   }
+
+   /** Map the argument principal from the deployment environment principal
+    to the developer environment. This is called by the EJB context
+    getCallerPrincipal() to return the Principal as described by
+    the EJB developer domain.
+    @return a Principal object that is valid in the deployment environment
+    if one exists. If no Subject exists or the Subject has no principals
+    then the argument principal is returned.
+    */
+   public Principal getPrincipal(Principal principal)
+   {
+      if (domainCache == null)
+         return principal;
+      Principal result = principal;
+      // Get the CallerPrincipal group member
+      synchronized (domainCache)
+      {
+         DomainInfo info = getCacheInfo(principal, false);
+         if (trace)
+            log.trace("getPrincipal, cache info: " + info);
+         if (info != null)
+         {
+            result = info.callerPrincipal;
+            // If the mapping did not have a callerPrincipal just use principal
+            if (result == null)
+               result = principal;
+            info.release();
+         }
+      }
+
+      return result;
+   }
+
+   /** 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 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.
+    
+    @see java.security.acl.Group;
+    @see Subject#getPrincipals()
+    */
+   public boolean doesUserHaveRole(Principal principal, Set<Principal> rolePrincipals)
+   {
+      AuthorizationManager am = SecurityUtil.getAuthorizationManager(securityDomain,
+            SecurityConstants.JAAS_CONTEXT_ROOT);
+      return am.doesUserHaveRole(principal, rolePrincipals);
+   }
+
+   /** 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.
+   */
+   @SuppressWarnings("deprecation")
+   public Set<Principal> getUserRoles(Principal principal)
+   {
+      AuthorizationManager am = SecurityUtil.getAuthorizationManager(securityDomain,
+            SecurityConstants.JAAS_CONTEXT_ROOT);
+      return am.getUserRoles(principal);
+   }
+
+   /**
+    * @see AuthenticationManager#getTargetPrincipal(Principal,Map)
+    */
+   public Principal getTargetPrincipal(Principal anotherDomainPrincipal, Map<String, Object> contextMap)
+   {
+      throw new RuntimeException("Not implemented yet");
+   }
+
+   /** Currently this simply calls defaultLogin() to do a JAAS login using the
+    security domain name as the login module configuration name.
+    
+    * @param principal - the user id to authenticate
+    * @param credential - an opaque credential.
+    * @return false on failure, true on success.
+    */
+   private boolean authenticate(Principal principal, Object credential, Subject theSubject)
+   {
+      Subject subject = null;
+      boolean authenticated = false;
+      LoginException authException = null;
+
+      try
+      {
+         // Validate the principal using the login configuration for this domain
+         LoginContext lc = defaultLogin(principal, credential);
+         subject = lc.getSubject();
+
+         // Set the current subject if login was successful
+         if (subject != null)
+         {
+            // Copy the current subject into theSubject
+            if (theSubject != null)
+            {
+               SubjectActions.copySubject(subject, theSubject, false, this.deepCopySubjectOption);
+            }
+            else
+            {
+               theSubject = subject;
+            }
+
+            authenticated = true;
+            // Build the Subject based DomainInfo cache value
+            updateCache(lc, subject, principal, credential);
+         }
+      }
+      catch (LoginException e)
+      {
+         // Don't log anonymous user failures unless trace level logging is on
+         if (principal != null && principal.getName() != null || trace)
+            log.trace("Login failure", e);
+         authException = e;
+      }
+      // Set the security association thread context info exception
+      SubjectActions.setContextInfo("org.jboss.security.exception", authException);
+
+      return authenticated;
+   }
+
+   /** Pass the security info to the login modules configured for
+    this security domain using our SecurityAssociationHandler.
+    @return The authenticated Subject if successful.
+    @exception LoginException throw if login fails for any reason.
+    */
+   private LoginContext defaultLogin(Principal principal, Object credential) throws LoginException
+   {
+      /* 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
+      {
+         theHandler = (CallbackHandler) handler.getClass().newInstance();
+         setSecurityInfo.invoke(theHandler, securityInfo);
+      }
+      catch (Throwable e)
+      {
+         if (trace)
+            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;
+      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;
+   }
+
+   /** Validate the cache credential value against the provided credential
+    */
+   @SuppressWarnings("unchecked")
+   private boolean validateCache(DomainInfo info, Object credential, Subject theSubject)
+   {
+      if (trace)
+      {
+         StringBuffer tmp = new StringBuffer("Begin validateCache, info=");
+         tmp.append(info.toString());
+         tmp.append(";credential.class=");
+         if (credential != null)
+         {
+            Class c = credential.getClass();
+            tmp.append(c.getName());
+            tmp.append('@');
+            tmp.append(System.identityHashCode(c));
+         }
+         else
+         {
+            tmp.append("null");
+         }
+         log.trace(tmp.toString());
+      }
+
+      Object subjectCredential = info.credential;
+      boolean isValid = false;
+      // Check for a null credential as can be the case for an anonymous user
+      if (credential == null || subjectCredential == null)
+      {
+         // Both credentials must be null
+         isValid = (credential == null) && (subjectCredential == null);
+      }
+      // See if the credential is assignable to the cache value
+      else if (subjectCredential.getClass().isAssignableFrom(credential.getClass()))
+      {
+         /* Validate the credential by trying Comparable, char[], byte[],
+          Object[], and finally Object.equals()
+          */
+         if (subjectCredential instanceof Comparable)
+         {
+            Comparable c = (Comparable) subjectCredential;
+            isValid = c.compareTo(credential) == 0;
+         }
+         else if (subjectCredential instanceof char[])
+         {
+            char[] a1 = (char[]) subjectCredential;
+            char[] a2 = (char[]) credential;
+            isValid = Arrays.equals(a1, a2);
+         }
+         else if (subjectCredential instanceof byte[])
+         {
+            byte[] a1 = (byte[]) subjectCredential;
+            byte[] a2 = (byte[]) credential;
+            isValid = Arrays.equals(a1, a2);
+         }
+         else if (subjectCredential.getClass().isArray())
+         {
+            Object[] a1 = (Object[]) subjectCredential;
+            Object[] a2 = (Object[]) credential;
+            isValid = Arrays.equals(a1, a2);
+         }
+         else
+         {
+            isValid = subjectCredential.equals(credential);
+         }
+      }
+      else if (subjectCredential instanceof char[] && credential instanceof String)
+      {
+         char[] a1 = (char[]) subjectCredential;
+         char[] a2 = ((String) credential).toCharArray();
+         isValid = Arrays.equals(a1, a2);
+      }
+      else if (subjectCredential instanceof String && credential instanceof char[])
+      {
+         char[] a1 = ((String) subjectCredential).toCharArray();
+         char[] a2 = (char[]) credential;
+         isValid = Arrays.equals(a1, a2);
+      }
+
+      // If the credentials match, set the thread's active Subject
+      if (isValid)
+      {
+         // Copy the current subject into theSubject
+         if (theSubject != null)
+         {
+            SubjectActions.copySubject(info.subject, theSubject, false, this.deepCopySubjectOption);
+         }
+      }
+      if (trace)
+         log.trace("End validateCache, isValid=" + isValid);
+
+      return isValid;
+   }
+
+   /** 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 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.
+    @param allowRefresh - a flag indicating if the cache access should flush
+    any expired entries.
+    */
+   private DomainInfo getCacheInfo(Principal principal, boolean allowRefresh)
+   {
+      if (domainCache == null)
+         return null;
+
+      DomainInfo cacheInfo = null;
+      synchronized (domainCache)
+      {
+         if (allowRefresh == true)
+            cacheInfo = (DomainInfo) domainCache.get(principal);
+         else
+            cacheInfo = (DomainInfo) domainCache.peek(principal);
+         if (cacheInfo != null)
+            cacheInfo.acquire();
+      }
+      return cacheInfo;
+   }
+
+   private Subject updateCache(LoginContext lc, Subject subject, Principal principal, Object credential)
+   {
+      // If we don't have a cache there is nothing to update
+      if (domainCache == null)
+         return subject;
+
+      long lifetime = 0;
+      if (domainCache instanceof TimedCachePolicy)
+      {
+         TimedCachePolicy cache = (TimedCachePolicy) domainCache;
+         lifetime = cache.getDefaultLifetime();
+      }
+      DomainInfo info = new DomainInfo(lifetime);
+      info.loginCtx = lc;
+      info.subject = new Subject();
+      SubjectActions.copySubject(subject, info.subject, true, this.deepCopySubjectOption);
+      info.credential = credential;
+
+      if (trace)
+      {
+         log.trace("updateCache, inputSubject=" + SubjectActions.toString(subject) + ", cacheSubject="
+               + SubjectActions.toString(info.subject));
+      }
+
+      /* Get the Subject callerPrincipal by looking for a Group called
+         'CallerPrincipal'
+       */
+      Set<Group> subjectGroups = subject.getPrincipals(Group.class);
+      Iterator<Group> iter = subjectGroups.iterator();
+      while (iter.hasNext())
+      {
+         Group grp = iter.next();
+         String name = grp.getName();
+         if (name.equals("CallerPrincipal"))
+         {
+            Enumeration<? extends Principal> members = grp.members();
+            if (members.hasMoreElements())
+               info.callerPrincipal = members.nextElement();
+         }
+      }
+
+      /* Handle null principals with no callerPrincipal. This is an indication
+         of an user that has not provided any authentication info, but
+         has been authenticated by the domain login module stack. Here we look
+         for the first non-Group Principal and use that.
+       */
+      if (principal == null && info.callerPrincipal == null)
+      {
+         Set<Principal> subjectPrincipals = subject.getPrincipals(Principal.class);
+         Iterator<? extends Principal> iterPrincipals = subjectPrincipals.iterator();
+         while (iterPrincipals.hasNext())
+         {
+            Principal p = iterPrincipals.next();
+            if ((p instanceof Group) == false)
+               info.callerPrincipal = p;
+         }
+      }
+
+      /* If the user already exists another login is active. Currently
+         only one is allowed so remove the old and insert the new. Synchronize
+         on the domainCache to ensure the removal and addition are an atomic
+         operation so that getCacheInfo cannot see stale data.
+       */
+      synchronized (domainCache)
+      {
+         if (domainCache.peek(principal) != null)
+            domainCache.remove(principal);
+         domainCache.insert(principal, info);
+         if (trace)
+            log.trace("Inserted cache info: " + info);
+      }
+      return info.subject;
+   }
+}
\ No newline at end of file

Deleted: projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManagerBase.java
===================================================================
--- projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManagerBase.java	2008-06-12 20:01:47 UTC (rev 74501)
+++ projects/security/security-jboss-sx/trunk/jbosssx/src/main/org/jboss/security/plugins/auth/SynchronizedJaasSecurityManagerBase.java	2008-06-12 20:08:28 UTC (rev 74502)
@@ -1,615 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2005, JBoss Inc., and individual contributors as indicated
-* by the @authors tag. See the copyright.txt in the distribution for a
-* full listing of individual contributors.
-*
-* This is free software; you can redistribute it and/or modify it
-* under the terms of the GNU Lesser General Public License as
-* published by the Free Software Foundation; either version 2.1 of
-* the License, or (at your option) any later version.
-*
-* This software is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this software; if not, write to the Free
-* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-*/
-package org.jboss.security.plugins.auth;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.UndeclaredThrowableException;
-import java.security.Principal;
-import java.security.acl.Group;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.jboss.logging.Logger;
-import org.jboss.security.AuthenticationManager;
-import org.jboss.security.AuthorizationManager;
-import org.jboss.security.RealmMapping;
-import org.jboss.security.SecurityConstants;
-import org.jboss.security.SecurityContext;
-import org.jboss.security.SecurityContextAssociation;
-import org.jboss.security.SecurityUtil;
-import org.jboss.security.SubjectSecurityManager;
-import org.jboss.security.auth.callback.SecurityAssociationHandler;
-import org.jboss.security.plugins.auth.JaasSecurityManagerBase.DomainInfo;
-import org.jboss.util.CachePolicy;
-import org.jboss.util.TimedCachePolicy;
-
-/** The JaasSecurityManager is responsible both for authenticating credentials
- associated with principals and for role mapping. This implementation relies
- on the JAAS LoginContext/LoginModules associated with the security
- domain name associated with the class for authentication,
- and the context JAAS Subject object for role mapping.
- 
- @see #isValid(Principal, Object, Subject)
- @see #getPrincipal(Principal)
- @see #doesUserHaveRole(Principal, Set)
- 
- @author <a href="on at ibis.odessa.ua">Oleg Nitz</a>
- @author Scott.Stark at jboss.org
- @author Anil.Saldhana at jboss.org
- @author <a href="mmoyses at redhat.com">Marcus Moyses</a>
- @version $Revision: 62860 $
-*/
-public class SynchronizedJaasSecurityManagerBase implements SubjectSecurityManager, RealmMapping
-{
-   /** The name of the domain this instance is securing. It is used as
-    the appName into the SecurityPolicy.
-    */
-   private String securityDomain;
-
-   /** A cache of DomainInfo objects keyd by Principal. This is now
-    always set externally by our security manager service.
-    */
-   private CachePolicy domainCache;
-
-   /** The JAAS callback handler to use in defaultLogin */
-   private CallbackHandler handler;
-
-   /** The setSecurityInfo(Principal, Object) method of the handler obj */
-   private transient Method setSecurityInfo;
-
-   /** The flag to indicate that the Subject sets need to be deep copied*/
-   private boolean deepCopySubjectOption = false;
-
-   /** The log4j category for the security manager domain
-    */
-   protected Logger log;
-
-   protected boolean trace;
-
-   /** Creates a default JaasSecurityManager for with a securityDomain
-    name of 'other'.
-    */
-   public SynchronizedJaasSecurityManagerBase()
-   {
-      this("other", new SecurityAssociationHandler());
-   }
-
-   /** Creates a JaasSecurityManager for with a securityDomain
-    name of that given by the 'securityDomain' argument.
-    @param securityDomain the name of the security domain
-    @param handler the JAAS callback handler instance to use
-    @exception UndeclaredThrowableException thrown if handler does not
-      implement a setSecurityInfo(Princpal, Object) method
-    */
-   public SynchronizedJaasSecurityManagerBase(String securityDomain, CallbackHandler handler)
-   {
-      this.securityDomain = securityDomain;
-      this.handler = handler;
-      String categoryName = getClass().getName() + '.' + securityDomain;
-      this.log = Logger.getLogger(categoryName);
-      this.trace = log.isTraceEnabled();
-
-      // Get the setSecurityInfo(Principal principal, Object credential) method
-      Class<?>[] sig =
-      {Principal.class, Object.class};
-      try
-      {
-         setSecurityInfo = handler.getClass().getMethod("setSecurityInfo", sig);
-      }
-      catch (Exception e)
-      {
-         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, Subject)
-    */
-   public void setCachePolicy(CachePolicy domainCache)
-   {
-      this.domainCache = domainCache;
-      log.debug("CachePolicy set to: " + domainCache);
-   }
-
-   /**
-    * Flag to specify if deep copy of subject sets needs to be 
-    * enabled
-    * 
-    * @param flag
-    */
-   public void setDeepCopySubjectOption(Boolean flag)
-   {
-      log.debug("setDeepCopySubjectOption=" + flag);
-      this.deepCopySubjectOption = (flag == Boolean.TRUE);
-   }
-
-   /** Not really used anymore as the security manager service manages the
-    security domain authentication caches.
-    */
-   public void flushCache()
-   {
-      if (domainCache != null)
-         domainCache.flush();
-   }
-
-   /** Get the name of the security domain associated with this security mgr.
-    @return Name of the security manager security domain.
-    */
-   public String getSecurityDomain()
-   {
-      return securityDomain;
-   }
-
-   /** Get the currently authenticated Subject. This is a thread local
-    property shared across all JaasSecurityManager instances.
-    @return The Subject authenticated in the current thread if one
-    exists, null otherwise.
-    */
-   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();
-      Subject subj = null;
-      SecurityContext sc = SecurityContextAssociation.getSecurityContext();
-      if (sc != null)
-      {
-         subj = sc.getUtil().getSubject();
-      }
-      return subj;
-   }
-
-   /** Validate that the given credential is correct for principal. This
-    returns the value from invoking isValid(principal, credential, null).
-    @param principal - the security domain principal attempting access
-    @param credential - the proof of identity offered by the principal
-    @return true if the principal was authenticated, false otherwise.
-    */
-   public boolean isValid(Principal principal, Object credential)
-   {
-      return isValid(principal, credential, null);
-   }
-
-   /** Validate that the given credential is correct for principal. This first
-    will check the current CachePolicy object if one exists to see if the
-    user's cached credentials match the given credential. If there is no
-    credential cache or the cache information is invalid or does not match,
-    the user is authenticated against the JAAS login modules configured for
-    the security domain.
-    @param principal - the security domain principal attempting access
-    @param credential  the proof of identity offered by the principal
-    @param activeSubject - if not null, a Subject that will be populated with
-      the state of the authenticated Subject.
-    @return true if the principal was authenticated, false otherwise.
-    */
-   public boolean isValid(Principal principal, Object credential, Subject activeSubject)
-   {
-      // Check the cache first
-      DomainInfo cacheInfo = getCacheInfo(principal, true);
-      if (trace)
-         log.trace("Begin isValid, principal:" + principal + ", cache info: " + cacheInfo);
-
-      boolean isValid = false;
-      if (principal != null && principal.getName() != null)
-      {
-         synchronized (principal.getName().intern())
-         {
-            if (cacheInfo != null)
-            {
-               isValid = validateCache(cacheInfo, credential, activeSubject);
-               cacheInfo.release();
-            }
-            if (isValid == false)
-               isValid = authenticate(principal, credential, activeSubject);
-         }
-      }
-      else
-      {
-         if (cacheInfo != null)
-            cacheInfo.release();
-         isValid = authenticate(principal, credential, activeSubject);
-      }
-      if (trace)
-         log.trace("End isValid, " + isValid);
-      return isValid;
-   }
-
-   /** Map the argument principal from the deployment environment principal
-    to the developer environment. This is called by the EJB context
-    getCallerPrincipal() to return the Principal as described by
-    the EJB developer domain.
-    @return a Principal object that is valid in the deployment environment
-    if one exists. If no Subject exists or the Subject has no principals
-    then the argument principal is returned.
-    */
-   public Principal getPrincipal(Principal principal)
-   {
-      if (domainCache == null)
-         return principal;
-      Principal result = principal;
-      // Get the CallerPrincipal group member
-      synchronized (domainCache)
-      {
-         DomainInfo info = getCacheInfo(principal, false);
-         if (trace)
-            log.trace("getPrincipal, cache info: " + info);
-         if (info != null)
-         {
-            result = info.callerPrincipal;
-            // If the mapping did not have a callerPrincipal just use principal
-            if (result == null)
-               result = principal;
-            info.release();
-         }
-      }
-
-      return result;
-   }
-
-   /** 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 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.
-    
-    @see java.security.acl.Group;
-    @see Subject#getPrincipals()
-    */
-   public boolean doesUserHaveRole(Principal principal, Set<Principal> rolePrincipals)
-   {
-      AuthorizationManager am = SecurityUtil.getAuthorizationManager(securityDomain,
-            SecurityConstants.JAAS_CONTEXT_ROOT);
-      return am.doesUserHaveRole(principal, rolePrincipals);
-   }
-
-   /** 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.
-   */
-   @SuppressWarnings("deprecation")
-   public Set<Principal> getUserRoles(Principal principal)
-   {
-      AuthorizationManager am = SecurityUtil.getAuthorizationManager(securityDomain,
-            SecurityConstants.JAAS_CONTEXT_ROOT);
-      return am.getUserRoles(principal);
-   }
-
-   /**
-    * @see AuthenticationManager#getTargetPrincipal(Principal,Map)
-    */
-   public Principal getTargetPrincipal(Principal anotherDomainPrincipal, Map<String, Object> contextMap)
-   {
-      throw new RuntimeException("Not implemented yet");
-   }
-
-   /** Currently this simply calls defaultLogin() to do a JAAS login using the
-    security domain name as the login module configuration name.
-    
-    * @param principal - the user id to authenticate
-    * @param credential - an opaque credential.
-    * @return false on failure, true on success.
-    */
-   private boolean authenticate(Principal principal, Object credential, Subject theSubject)
-   {
-      Subject subject = null;
-      boolean authenticated = false;
-      LoginException authException = null;
-
-      try
-      {
-         // Validate the principal using the login configuration for this domain
-         LoginContext lc = defaultLogin(principal, credential);
-         subject = lc.getSubject();
-
-         // Set the current subject if login was successful
-         if (subject != null)
-         {
-            // Copy the current subject into theSubject
-            if (theSubject != null)
-            {
-               SubjectActions.copySubject(subject, theSubject, false, this.deepCopySubjectOption);
-            }
-            else
-            {
-               theSubject = subject;
-            }
-
-            authenticated = true;
-            // Build the Subject based DomainInfo cache value
-            updateCache(lc, subject, principal, credential);
-         }
-      }
-      catch (LoginException e)
-      {
-         // Don't log anonymous user failures unless trace level logging is on
-         if (principal != null && principal.getName() != null || trace)
-            log.trace("Login failure", e);
-         authException = e;
-      }
-      // Set the security association thread context info exception
-      SubjectActions.setContextInfo("org.jboss.security.exception", authException);
-
-      return authenticated;
-   }
-
-   /** Pass the security info to the login modules configured for
-    this security domain using our SecurityAssociationHandler.
-    @return The authenticated Subject if successful.
-    @exception LoginException throw if login fails for any reason.
-    */
-   private LoginContext defaultLogin(Principal principal, Object credential) throws LoginException
-   {
-      /* 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
-      {
-         theHandler = (CallbackHandler) handler.getClass().newInstance();
-         setSecurityInfo.invoke(theHandler, securityInfo);
-      }
-      catch (Throwable e)
-      {
-         if (trace)
-            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;
-      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;
-   }
-
-   /** Validate the cache credential value against the provided credential
-    */
-   @SuppressWarnings("unchecked")
-   private boolean validateCache(DomainInfo info, Object credential, Subject theSubject)
-   {
-      if (trace)
-      {
-         StringBuffer tmp = new StringBuffer("Begin validateCache, info=");
-         tmp.append(info.toString());
-         tmp.append(";credential.class=");
-         if (credential != null)
-         {
-            Class c = credential.getClass();
-            tmp.append(c.getName());
-            tmp.append('@');
-            tmp.append(System.identityHashCode(c));
-         }
-         else
-         {
-            tmp.append("null");
-         }
-         log.trace(tmp.toString());
-      }
-
-      Object subjectCredential = info.credential;
-      boolean isValid = false;
-      // Check for a null credential as can be the case for an anonymous user
-      if (credential == null || subjectCredential == null)
-      {
-         // Both credentials must be null
-         isValid = (credential == null) && (subjectCredential == null);
-      }
-      // See if the credential is assignable to the cache value
-      else if (subjectCredential.getClass().isAssignableFrom(credential.getClass()))
-      {
-         /* Validate the credential by trying Comparable, char[], byte[],
-          Object[], and finally Object.equals()
-          */
-         if (subjectCredential instanceof Comparable)
-         {
-            Comparable c = (Comparable) subjectCredential;
-            isValid = c.compareTo(credential) == 0;
-         }
-         else if (subjectCredential instanceof char[])
-         {
-            char[] a1 = (char[]) subjectCredential;
-            char[] a2 = (char[]) credential;
-            isValid = Arrays.equals(a1, a2);
-         }
-         else if (subjectCredential instanceof byte[])
-         {
-            byte[] a1 = (byte[]) subjectCredential;
-            byte[] a2 = (byte[]) credential;
-            isValid = Arrays.equals(a1, a2);
-         }
-         else if (subjectCredential.getClass().isArray())
-         {
-            Object[] a1 = (Object[]) subjectCredential;
-            Object[] a2 = (Object[]) credential;
-            isValid = Arrays.equals(a1, a2);
-         }
-         else
-         {
-            isValid = subjectCredential.equals(credential);
-         }
-      }
-      else if (subjectCredential instanceof char[] && credential instanceof String)
-      {
-         char[] a1 = (char[]) subjectCredential;
-         char[] a2 = ((String) credential).toCharArray();
-         isValid = Arrays.equals(a1, a2);
-      }
-      else if (subjectCredential instanceof String && credential instanceof char[])
-      {
-         char[] a1 = ((String) subjectCredential).toCharArray();
-         char[] a2 = (char[]) credential;
-         isValid = Arrays.equals(a1, a2);
-      }
-
-      // If the credentials match, set the thread's active Subject
-      if (isValid)
-      {
-         // Copy the current subject into theSubject
-         if (theSubject != null)
-         {
-            SubjectActions.copySubject(info.subject, theSubject, false, this.deepCopySubjectOption);
-         }
-      }
-      if (trace)
-         log.trace("End validateCache, isValid=" + isValid);
-
-      return isValid;
-   }
-
-   /** 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 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.
-    @param allowRefresh - a flag indicating if the cache access should flush
-    any expired entries.
-    */
-   private DomainInfo getCacheInfo(Principal principal, boolean allowRefresh)
-   {
-      if (domainCache == null)
-         return null;
-
-      DomainInfo cacheInfo = null;
-      synchronized (domainCache)
-      {
-         if (allowRefresh == true)
-            cacheInfo = (DomainInfo) domainCache.get(principal);
-         else
-            cacheInfo = (DomainInfo) domainCache.peek(principal);
-         if (cacheInfo != null)
-            cacheInfo.acquire();
-      }
-      return cacheInfo;
-   }
-
-   private Subject updateCache(LoginContext lc, Subject subject, Principal principal, Object credential)
-   {
-      // If we don't have a cache there is nothing to update
-      if (domainCache == null)
-         return subject;
-
-      long lifetime = 0;
-      if (domainCache instanceof TimedCachePolicy)
-      {
-         TimedCachePolicy cache = (TimedCachePolicy) domainCache;
-         lifetime = cache.getDefaultLifetime();
-      }
-      DomainInfo info = new DomainInfo(lifetime);
-      info.loginCtx = lc;
-      info.subject = new Subject();
-      SubjectActions.copySubject(subject, info.subject, true, this.deepCopySubjectOption);
-      info.credential = credential;
-
-      if (trace)
-      {
-         log.trace("updateCache, inputSubject=" + SubjectActions.toString(subject) + ", cacheSubject="
-               + SubjectActions.toString(info.subject));
-      }
-
-      /* Get the Subject callerPrincipal by looking for a Group called
-         'CallerPrincipal'
-       */
-      Set<Group> subjectGroups = subject.getPrincipals(Group.class);
-      Iterator<Group> iter = subjectGroups.iterator();
-      while (iter.hasNext())
-      {
-         Group grp = iter.next();
-         String name = grp.getName();
-         if (name.equals("CallerPrincipal"))
-         {
-            Enumeration<? extends Principal> members = grp.members();
-            if (members.hasMoreElements())
-               info.callerPrincipal = members.nextElement();
-         }
-      }
-
-      /* Handle null principals with no callerPrincipal. This is an indication
-         of an user that has not provided any authentication info, but
-         has been authenticated by the domain login module stack. Here we look
-         for the first non-Group Principal and use that.
-       */
-      if (principal == null && info.callerPrincipal == null)
-      {
-         Set<Principal> subjectPrincipals = subject.getPrincipals(Principal.class);
-         Iterator<? extends Principal> iterPrincipals = subjectPrincipals.iterator();
-         while (iterPrincipals.hasNext())
-         {
-            Principal p = iterPrincipals.next();
-            if ((p instanceof Group) == false)
-               info.callerPrincipal = p;
-         }
-      }
-
-      /* If the user already exists another login is active. Currently
-         only one is allowed so remove the old and insert the new. Synchronize
-         on the domainCache to ensure the removal and addition are an atomic
-         operation so that getCacheInfo cannot see stale data.
-       */
-      synchronized (domainCache)
-      {
-         if (domainCache.peek(principal) != null)
-            domainCache.remove(principal);
-         domainCache.insert(principal, info);
-         if (trace)
-            log.trace("Inserted cache info: " + info);
-      }
-      return info.subject;
-   }
-}
\ No newline at end of file




More information about the jboss-cvs-commits mailing list