[jboss-cvs] JBossAS SVN: r79237 - in projects/security/security-negotiation/trunk: jboss-negotiation/src/main/java/org/jboss/security/negotiation/spnego and 4 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Tue Oct 7 18:30:51 EDT 2008


Author: darran.lofthouse at jboss.com
Date: 2008-10-07 18:30:51 -0400 (Tue, 07 Oct 2008)
New Revision: 79237

Added:
   projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/AdvancedLdapLoginModule.java
Modified:
   projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/NegotiationAuthenticator.java
   projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/common/MessageTrace.java
   projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/common/NegotiationContext.java
   projects/security/security-negotiation/trunk/jboss-negotiation-spnego/src/main/java/org/jboss/security/negotiation/spnego/SPNEGOAuthenticator.java
   projects/security/security-negotiation/trunk/jboss-negotiation-spnego/src/main/java/org/jboss/security/negotiation/spnego/SPNEGOLoginModule.java
   projects/security/security-negotiation/trunk/jboss-negotiation-toolkit/src/main/java/org/jboss/security/negotiation/toolkit/SecuredServlet.java
   projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/spnego/AdvancedLdapLoginModule.java
Log:
[SECURITY-270] Refactoring to be MessageFactory based.

Copied: projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/AdvancedLdapLoginModule.java (from rev 78496, projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/spnego/AdvancedLdapLoginModule.java)
===================================================================
--- projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/AdvancedLdapLoginModule.java	                        (rev 0)
+++ projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/AdvancedLdapLoginModule.java	2008-10-07 22:30:51 UTC (rev 79237)
@@ -0,0 +1,744 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file 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.negotiation;
+
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.acl.Group;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.management.ObjectName;
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.jboss.security.SimpleGroup;
+import org.jboss.security.auth.spi.AbstractServerLoginModule;
+import org.jboss.security.negotiation.prototype.DecodeAction;
+
+/**
+ * Another LDAP LoginModule to take into account requirements
+ * for different authentication mechanisms and full support
+ * for password-stacking set to useFirstPass.
+ * 
+ * This is essentially a complete refactoring of the LdapExtLoginModule
+ * but with enough restructuring to separate out the three login steps: -
+ *  -1 Find the user
+ *  -2 Authenticate as the user
+ *  -3 Find the users roles
+ * Configuration should allow for any of the three actions to be
+ * skipped based on the requirements for the environment making
+ * use of this login module. 
+ *
+ * 
+ * @author darran.lofthouse at jboss.com
+ * @since 3rd July 2008
+ */
+public class AdvancedLdapLoginModule extends AbstractServerLoginModule
+{
+
+   /*
+    * Configuration Option Constants 
+    */
+
+   // Search Context Settings
+   private static final String BIND_AUTHENTICATION = "bindAuthentication";
+
+   private static final String BIND_DN = "bindDN";
+
+   private static final String BIND_CREDENTIAL = "bindCredential";
+
+   private static final String SECURITY_DOMAIN = "jaasSecurityDomain";
+
+   // User Search Settings
+   private static final String BASE_CTX_DN = "baseCtxDN";
+
+   private static final String BASE_FILTER = "baseFilter";
+
+   private static final String SEARCH_TIME_LIMIT = "searchTimeLimit";
+
+   // Role Search Settings
+   private static final String ROLES_CTS_DN = "rolesCtxDN";
+
+   private static final String ROLE_FILTER = "roleFilter";
+
+   private static final String RECURSE_ROLES = "recurseRoles";
+
+   private static final String ROLE_ATTRIBUTE_ID = "roleAttributeID";
+
+   private static final String ROLE_ATTRIBUTE_IS_DN = "roleAttributeIsDN";
+
+   private static final String ROLE_NAME_ATTRIBUTE_ID = "roleNameAttributeID";
+
+   private static final String ROLE_SEARCH_SCOPE = "searchScope";
+
+   // Authentication Settings
+   private static final String ALLOW_EMPTY_PASSWORD = "allowEmptyPassword";
+
+   /*
+    * Other Constants
+    */
+
+   private static final String AUTH_TYPE_GSSAPI = "GSSAPI";
+
+   private static final String AUTH_TYPE_SIMPLE = "simple";
+
+   private static final String DEFAULT_LDAP_CTX_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
+
+   private static final String DEFAULT_URL = "ldap://localhost:389";
+
+   private static final String DEFAULT_SSL_URL = "ldap://localhost:686";
+
+   private static final String PROTOCOL_SSL = "SSL";
+
+   private static final String OBJECT_SCOPE = "OBJECT_SCOPE";
+
+   private static final String ONELEVEL_SCOPE = "ONELEVEL_SCOPE";
+
+   private static final String SUBTREE_SCOPE = "SUBTREE_SCOPE";
+
+   /*
+    * Configuration Options
+    */
+   // Search Context Settings
+   protected String bindAuthentication;
+
+   protected String bindDn;
+
+   protected String bindCredential;
+
+   protected String jaasSecurityDomain;
+
+   // User Search Settings
+   protected String baseCtxDN;
+
+   protected String baseFilter;
+
+   protected int searchTimeLimit = 10000;
+
+   protected SearchControls userSearchControls;
+
+   // Role Search Settings
+   protected String rolesCtxDN;
+
+   protected String roleFilter;
+
+   protected boolean recurseRoles;
+
+   protected SearchControls roleSearchControls;
+
+   protected String roleAttributeID;
+
+   protected boolean roleAttributeIsDN;
+
+   protected String roleNameAttributeID;
+
+   // Authentication Settings
+   protected boolean allowEmptyPassword;
+
+   /*
+    * Module State 
+    */
+   /** The login identity */
+   private Principal identity;
+
+   /** The proof of login identity */
+   private char[] credential;
+
+   private SimpleGroup userRoles = new SimpleGroup("Roles");
+
+   private Set<String> processedRoleDNs = new HashSet<String>();
+
+   @Override
+   public void initialize(Subject subject, CallbackHandler handler, Map sharedState, Map options)
+   {
+      super.initialize(subject, handler, sharedState, options);
+
+      // Search Context Settings
+      bindAuthentication = (String) options.get(BIND_AUTHENTICATION);
+      bindDn = (String) options.get(BIND_DN);
+      bindCredential = (String) options.get(BIND_CREDENTIAL);
+      jaasSecurityDomain = (String) options.get(SECURITY_DOMAIN);
+
+      // User Search Settings
+      baseCtxDN = (String) options.get(BASE_CTX_DN);
+      baseFilter = (String) options.get(BASE_FILTER);
+
+      String temp = (String) options.get(SEARCH_TIME_LIMIT);
+      if (temp != null)
+      {
+         try
+         {
+            searchTimeLimit = Integer.parseInt(temp);
+         }
+         catch (NumberFormatException e)
+         {
+            log.warn("Failed to parse: " + temp + ", using searchTimeLimit=" + searchTimeLimit);
+         }
+      }
+
+      userSearchControls = new SearchControls();
+      userSearchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+      userSearchControls.setReturningAttributes(new String[0]);
+      userSearchControls.setTimeLimit(searchTimeLimit);
+
+      rolesCtxDN = (String) options.get(ROLES_CTS_DN);
+      roleFilter = (String) options.get(ROLE_FILTER);
+
+      temp = (String) options.get(RECURSE_ROLES);
+      recurseRoles = Boolean.parseBoolean(temp);
+
+      int searchScope = SearchControls.SUBTREE_SCOPE;
+      temp = (String) options.get(ROLE_SEARCH_SCOPE);
+      if (OBJECT_SCOPE.equalsIgnoreCase(temp))
+      {
+         searchScope = SearchControls.OBJECT_SCOPE;
+      }
+      else if (ONELEVEL_SCOPE.equalsIgnoreCase(temp))
+      {
+         searchScope = SearchControls.ONELEVEL_SCOPE;
+      }
+      if (SUBTREE_SCOPE.equalsIgnoreCase(temp))
+      {
+         searchScope = SearchControls.SUBTREE_SCOPE;
+      }
+
+      roleSearchControls = new SearchControls();
+      roleSearchControls.setSearchScope(searchScope);
+      roleSearchControls.setReturningAttributes(new String[0]);
+      roleSearchControls.setTimeLimit(searchTimeLimit);
+
+      roleAttributeID = (String) options.get(ROLE_ATTRIBUTE_ID);
+
+      temp = (String) options.get(ROLE_ATTRIBUTE_IS_DN);
+      roleAttributeIsDN = Boolean.parseBoolean(temp);
+
+      roleNameAttributeID = (String) options.get(ROLE_NAME_ATTRIBUTE_ID);
+
+      temp = (String) options.get(ALLOW_EMPTY_PASSWORD);
+      allowEmptyPassword = Boolean.parseBoolean(temp);
+
+   }
+
+   @Override
+   public boolean login() throws LoginException
+   {
+      Object result = null;
+
+      AuthorizeAction action = new AuthorizeAction();
+      if (AUTH_TYPE_GSSAPI.equals(bindAuthentication))
+      {
+         log.trace("Using GSSAPI to connect to LDAP");
+         LoginContext lc = new LoginContext(jaasSecurityDomain);
+         lc.login();
+         Subject serverSubject = lc.getSubject();
+
+         if (log.isDebugEnabled())
+         {
+            log.debug("Subject = " + serverSubject);
+            log.debug("Logged in '" + lc + "' LoginContext");
+         }
+
+         result = Subject.doAs(serverSubject, action);
+         lc.logout();
+      }
+      else
+      {
+         result = action.run();
+      }
+
+      if (result instanceof LoginException)
+      {
+         throw (LoginException) result;
+      }
+
+      return ((Boolean) result).booleanValue();
+   }
+
+   @Override
+   protected Principal getIdentity()
+   {
+      return identity;
+   }
+
+   @Override
+   protected Group[] getRoleSets() throws LoginException
+   {
+      Group[] roleSets =
+      {userRoles};
+      return roleSets;
+   }
+
+   protected Boolean innerLogin() throws LoginException
+   {
+      // Obtain the username and password
+      processIdentityAndCredential();
+      log.trace("Identity - " + getIdentity().getName());
+      // Initialise search ctx
+      String bindCredential = this.bindCredential;
+      if (AUTH_TYPE_GSSAPI.equals(bindAuthentication) == false)
+      {
+         if (jaasSecurityDomain != null && jaasSecurityDomain.length() > 0)
+         {
+            try
+            {
+               ObjectName serviceName = new ObjectName(jaasSecurityDomain);
+               char[] tmp = DecodeAction.decode(bindCredential, serviceName);
+               bindCredential = new String(tmp);
+            }
+            catch (Exception e)
+            {
+               LoginException le = new LoginException("Unabe to decode bindCredential");
+               le.initCause(e);
+               throw le;
+            }
+         }
+      }
+
+      LdapContext searchContext = null;
+
+      try
+      {
+         searchContext = constructLdapContext(bindDn, bindCredential, bindAuthentication);
+         log.debug("Obtained LdapContext");
+
+         // Search for user in LDAP
+         String userDN = findUserDN(searchContext);
+
+         // If authentication required authenticate as user
+         if (super.loginOk == false)
+         {
+            authenticate(userDN);
+         }
+
+         if (super.loginOk)
+         {
+            // Search for roles in LDAP
+            rolesSearch(searchContext, userDN);
+         }
+      }
+      finally
+      {
+         if (searchContext != null)
+         {
+            try
+            {
+               searchContext.close();
+            }
+            catch (NamingException e)
+            {
+               log.warn("Error closing context", e);
+            }
+         }
+      }
+
+      return Boolean.valueOf(super.loginOk);
+   }
+
+   /**
+    * Either retrieve existing values based on useFirstPass or use 
+    * CallBackHandler to obtain the values.
+    */
+   protected void processIdentityAndCredential() throws LoginException
+   {
+      if (super.login() == true)
+      {
+         Object username = sharedState.get("javax.security.auth.login.name");
+         if (username instanceof Principal)
+            identity = (Principal) username;
+         else
+         {
+            String name = username.toString();
+            try
+            {
+               identity = createIdentity(name);
+            }
+            catch (Exception e)
+            {
+               log.debug("Failed to create principal", e);
+               throw new LoginException("Failed to create principal: " + e.getMessage());
+            }
+         }
+         // We have no further use for a credential so no need to retrieve it.
+      }
+      else
+      {
+         try
+         {
+            NameCallback nc = new NameCallback("User name: ", "guest");
+            PasswordCallback pc = new PasswordCallback("Password: ", false);
+            Callback[] callbacks =
+            {nc, pc};
+
+            callbackHandler.handle(callbacks);
+            String username = nc.getName();
+            identity = createIdentity(username);
+            credential = pc.getPassword();
+            pc.clearPassword();
+         }
+         catch (Exception e)
+         {
+            LoginException le = new LoginException("Unable to obtain username/credential");
+            le.initCause(e);
+            throw le;
+         }
+
+      }
+   }
+
+   protected LdapContext constructLdapContext(String dn, Object credential, String authentication)
+         throws LoginException
+   {
+      Properties env = new Properties();
+      Iterator iter = options.entrySet().iterator();
+      while (iter.hasNext())
+      {
+         Entry entry = (Entry) iter.next();
+         env.put(entry.getKey(), entry.getValue());
+      }
+
+      // Set defaults for key values if they are missing
+      String factoryName = env.getProperty(Context.INITIAL_CONTEXT_FACTORY);
+      if (factoryName == null)
+      {
+         factoryName = DEFAULT_LDAP_CTX_FACTORY;
+         env.setProperty(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+      }
+
+      // If this method is called with an authentication type then use that.
+      if (authentication != null && authentication.length() > 0)
+      {
+         env.setProperty(Context.SECURITY_AUTHENTICATION, authentication);
+      }
+      else
+      {
+         String authType = env.getProperty(Context.SECURITY_AUTHENTICATION);
+         if (authType == null)
+            env.setProperty(Context.SECURITY_AUTHENTICATION, AUTH_TYPE_SIMPLE);
+      }
+
+      String protocol = env.getProperty(Context.SECURITY_PROTOCOL);
+      String providerURL = (String) options.get(Context.PROVIDER_URL);
+      if (providerURL == null)
+      {
+         if (PROTOCOL_SSL.equals(protocol))
+         {
+            providerURL = DEFAULT_SSL_URL;
+         }
+         else
+         {
+            providerURL = DEFAULT_URL;
+         }
+         env.setProperty(Context.PROVIDER_URL, providerURL);
+      }
+
+      // Assume the caller of this method has checked the requirements for the principal and
+      // credentials.
+      if (dn != null)
+         env.setProperty(Context.SECURITY_PRINCIPAL, dn);
+      if (credential != null)
+         env.put(Context.SECURITY_CREDENTIALS, credential);
+      traceLdapEnv(env);
+      try
+      {
+         return new InitialLdapContext(env, null);
+      }
+      catch (NamingException e)
+      {
+         LoginException le = new LoginException("Unable to create new InitialLdapContext");
+         le.initCause(e);
+         throw le;
+      }
+   }
+
+   protected String findUserDN(LdapContext ctx) throws LoginException
+   {
+
+      if (baseCtxDN == null)
+      {
+         return getIdentity().getName();
+      }
+
+      try
+      {
+         NamingEnumeration results = null;
+
+         Object[] filterArgs =
+         {getIdentity().getName()};
+         results = ctx.search(baseCtxDN, baseFilter, filterArgs, userSearchControls);
+         if (results.hasMore() == false)
+         {
+            results.close();
+            throw new LoginException("Search of baseDN(" + baseCtxDN + ") found no matches");
+         }
+
+         SearchResult sr = (SearchResult) results.next();
+         String name = sr.getName();
+         String userDN = null;
+         if (sr.isRelative() == true)
+            userDN = name + "," + baseCtxDN;
+         else
+            throw new LoginException("Can't follow referal for authentication: " + name);
+
+         results.close();
+         results = null;
+
+         log.trace("findUserDN - " + userDN);
+         return userDN;
+      }
+      catch (NamingException e)
+      {
+         LoginException le = new LoginException("Unable to find user DN");
+         le.initCause(e);
+         throw le;
+      }
+   }
+
+   protected void authenticate(String userDN) throws LoginException
+   {
+      if (credential.length == 0)
+      {
+         if (allowEmptyPassword == false)
+         {
+            log.trace("Rejecting empty password.");
+            return;
+         }
+      }
+
+      try
+      {
+         LdapContext authContext = constructLdapContext(userDN, credential, null);
+         authContext.close();
+      }
+      catch (NamingException ne)
+      {
+         log.debug("Authentication failed - " + ne.getMessage());
+         LoginException le = new LoginException("Authentication failed");
+         le.initCause(ne);
+         throw le;
+      }
+
+      super.loginOk = true;
+      if (getUseFirstPass() == true)
+      { // Add the username and password to the shared state map
+         sharedState.put("javax.security.auth.login.name", getIdentity().getName());
+         sharedState.put("javax.security.auth.login.password", credential);
+      }
+
+   }
+
+   protected void rolesSearch(LdapContext searchContext, String dn) throws LoginException
+   {
+      Object[] filterArgs =
+      {getIdentity().getName(), dn};
+
+      NamingEnumeration results = null;
+      try
+      {
+         log.trace("rolesCtxDN=" + rolesCtxDN + " roleFilter=" + roleFilter + " filterArgs[0]=" + filterArgs[0]
+               + " filterArgs[1]=" + filterArgs[1]);
+
+         if (roleFilter != null && roleFilter.length() > 0)
+         {
+            results = searchContext.search(rolesCtxDN, roleFilter, filterArgs, roleSearchControls);
+            while (results.hasMore())
+            {
+               SearchResult sr = (SearchResult) results.next();
+               String resultDN = canonicalize(sr.getName());
+
+               obtainRole(searchContext, resultDN);
+            }
+         }
+         else
+         {
+            obtainRole(searchContext, dn);
+         }
+
+      }
+      catch (NamingException e)
+      {
+         LoginException le = new LoginException("Error finding roles");
+         le.initCause(e);
+         throw le;
+      }
+      finally
+      {
+         if (results != null)
+         {
+            try
+            {
+               results.close();
+            }
+            catch (NamingException e)
+            {
+               log.warn("Problem closing results", e);
+            }
+         }
+      }
+
+   }
+
+   protected void obtainRole(LdapContext searchContext, String dn) throws NamingException, LoginException
+   {
+      log.trace("rolesSearch resultDN = " + dn);
+
+      String[] attrNames =
+      {roleAttributeID};
+
+      Attributes result = searchContext.getAttributes(dn, attrNames);
+      if (result != null && result.size() > 0)
+      {
+         Attribute roles = result.get(roleAttributeID);
+         for (int n = 0; n < roles.size(); n++)
+         {
+            String roleName = (String) roles.get(n);
+            if (roleAttributeIsDN)
+            {
+               // Query the roleDN location for the value of roleNameAttributeID
+               String roleDN = roleName;
+               String[] returnAttribute =
+               {roleNameAttributeID};
+               log.trace("Using roleDN: " + roleDN);
+               try
+               {
+                  Attributes result2 = searchContext.getAttributes(roleDN, returnAttribute);
+                  Attribute roles2 = result2.get(roleNameAttributeID);
+                  if (roles2 != null)
+                  {
+                     for (int m = 0; m < roles2.size(); m++)
+                     {
+                        roleName = (String) roles2.get(m);
+                        addRole(roleName);
+                     }
+                  }
+               }
+               catch (NamingException e)
+               {
+                  log.trace("Failed to query roleNameAttrName", e);
+               }
+
+               if (recurseRoles)
+               {
+                  if (processedRoleDNs.contains(roleDN) == false)
+                  {
+                     processedRoleDNs.add(roleDN);
+                     log.trace("Recursive search for '" + roleDN + "'");
+                     rolesSearch(searchContext, roleDN);
+                  }
+                  else
+                  {
+                     log.trace("Already visited role '" + roleDN + "' ending recursion.");
+                  }
+               }
+            }
+            else
+            {
+               // The role attribute value is the role name
+               addRole(roleName);
+            }
+         }
+      }
+   }
+
+   protected void traceLdapEnv(Properties env)
+   {
+      if (log.isTraceEnabled())
+      {
+         Properties tmp = new Properties();
+         tmp.putAll(env);
+         String credentials = tmp.getProperty(Context.SECURITY_CREDENTIALS);
+         if (credentials != null && credentials.length() > 0)
+            tmp.setProperty(Context.SECURITY_CREDENTIALS, "***");
+         log.trace("Logging into LDAP server, env=" + tmp.toString());
+      }
+   }
+
+   private String canonicalize(String searchResult)
+   {
+      String result = searchResult;
+      int len = searchResult.length();
+
+      if (searchResult.endsWith("\""))
+      {
+         result = searchResult.substring(0, len - 1) + "," + rolesCtxDN + "\"";
+      }
+      else
+      {
+         result = searchResult + "," + rolesCtxDN;
+      }
+      return result;
+   }
+
+   private void addRole(String roleName)
+   {
+      if (roleName != null)
+      {
+         try
+         {
+            Principal p = super.createIdentity(roleName);
+            if (log.isTraceEnabled())
+               log.trace("Assign user '" + getIdentity().getName() + "' to role " + roleName);
+            userRoles.addMember(p);
+         }
+         catch (Exception e)
+         {
+            log.debug("Failed to create principal: " + roleName, e);
+         }
+      }
+   }
+
+   private class AuthorizeAction implements PrivilegedAction<Object>
+   {
+
+      public Object run()
+      {
+         try
+         {
+            return innerLogin();
+         }
+         catch (LoginException e)
+         {
+            return e;
+         }
+      }
+
+   }
+
+}

Modified: projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/spnego/AdvancedLdapLoginModule.java
===================================================================
--- projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/spnego/AdvancedLdapLoginModule.java	2008-10-07 22:24:51 UTC (rev 79236)
+++ projects/security/security-negotiation/trunk/jboss-negotiation/src/main/java/org/jboss/security/negotiation/spnego/AdvancedLdapLoginModule.java	2008-10-07 22:30:51 UTC (rev 79237)
@@ -21,724 +21,37 @@
  */
 package org.jboss.security.negotiation.spnego;
 
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.acl.Group;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.Map.Entry;
 
-import javax.management.ObjectName;
-import javax.naming.Context;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-import javax.naming.ldap.InitialLdapContext;
-import javax.naming.ldap.LdapContext;
 import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
 
-import org.jboss.security.SimpleGroup;
-import org.jboss.security.auth.spi.AbstractServerLoginModule;
-import org.jboss.security.negotiation.prototype.DecodeAction;
-
 /**
- * Another LDAP LoginModule to take into account requirements
- * for different authentication mechanisms and full support
- * for password-stacking set to useFirstPass.
- * 
- * This is essentially a complete refactoring of the LdapExtLoginModule
- * but with enough restructuring to separate out the three login steps: -
- *  -1 Find the user
- *  -2 Authenticate as the user
- *  -3 Find the users roles
- * Configuration should allow for any of the three actions to be
- * skipped based on the requirements for the environment making
- * use of this login module. 
+ * This login module has now been moved to the 'org.jboss.security.negotiation' package, 
+ * this class remains for backwards compatibility. 
  *
  * 
  * @author darran.lofthouse at jboss.com
  * @since 3rd July 2008
  */
-public class AdvancedLdapLoginModule extends AbstractServerLoginModule
+ at Deprecated
+public class AdvancedLdapLoginModule extends org.jboss.security.negotiation.AdvancedLdapLoginModule
 {
 
-   /*
-    * Configuration Option Constants 
-    */
+   private static boolean warned = false;
 
-   // Search Context Settings
-   private static final String BIND_AUTHENTICATION = "bindAuthentication";
-
-   private static final String BIND_DN = "bindDN";
-
-   private static final String BIND_CREDENTIAL = "bindCredential";
-
-   private static final String SECURITY_DOMAIN = "jaasSecurityDomain";
-
-   // User Search Settings
-   private static final String BASE_CTX_DN = "baseCtxDN";
-
-   private static final String BASE_FILTER = "baseFilter";
-
-   private static final String SEARCH_TIME_LIMIT = "searchTimeLimit";
-
-   // Role Search Settings
-   private static final String ROLES_CTS_DN = "rolesCtxDN";
-
-   private static final String ROLE_FILTER = "roleFilter";
-
-   private static final String RECURSE_ROLES = "recurseRoles";
-
-   private static final String ROLE_ATTRIBUTE_ID = "roleAttributeID";
-
-   private static final String ROLE_ATTRIBUTE_IS_DN = "roleAttributeIsDN";
-
-   private static final String ROLE_NAME_ATTRIBUTE_ID = "roleNameAttributeID";
-
-   private static final String ROLE_SEARCH_SCOPE = "searchScope";
-
-   // Authentication Settings
-   private static final String ALLOW_EMPTY_PASSWORD = "allowEmptyPassword";
-
-   /*
-    * Other Constants
-    */
-
-   private static final String AUTH_TYPE_GSSAPI = "GSSAPI";
-
-   private static final String AUTH_TYPE_SIMPLE = "simple";
-
-   private static final String DEFAULT_LDAP_CTX_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
-
-   private static final String DEFAULT_URL = "ldap://localhost:389";
-
-   private static final String DEFAULT_SSL_URL = "ldap://localhost:686";
-
-   private static final String PROTOCOL_SSL = "SSL";
-
-   private static final String OBJECT_SCOPE = "OBJECT_SCOPE";
-
-   private static final String ONELEVEL_SCOPE = "ONELEVEL_SCOPE";
-
-   private static final String SUBTREE_SCOPE = "SUBTREE_SCOPE";
-
-   /*
-    * Configuration Options
-    */
-   // Search Context Settings
-   protected String bindAuthentication;
-
-   protected String bindDn;
-
-   protected String bindCredential;
-
-   protected String jaasSecurityDomain;
-
-   // User Search Settings
-   protected String baseCtxDN;
-
-   protected String baseFilter;
-
-   protected int searchTimeLimit = 10000;
-
-   protected SearchControls userSearchControls;
-
-   // Role Search Settings
-   protected String rolesCtxDN;
-
-   protected String roleFilter;
-
-   protected boolean recurseRoles;
-
-   protected SearchControls roleSearchControls;
-
-   protected String roleAttributeID;
-
-   protected boolean roleAttributeIsDN;
-
-   protected String roleNameAttributeID;
-
-   // Authentication Settings
-   protected boolean allowEmptyPassword;
-
-   /*
-    * Module State 
-    */
-   /** The login identity */
-   private Principal identity;
-
-   /** The proof of login identity */
-   private char[] credential;
-
-   private SimpleGroup userRoles = new SimpleGroup("Roles");
-
-   private Set<String> processedRoleDNs = new HashSet<String>();
-
    @Override
    public void initialize(Subject subject, CallbackHandler handler, Map sharedState, Map options)
    {
       super.initialize(subject, handler, sharedState, options);
 
-      // Search Context Settings
-      bindAuthentication = (String) options.get(BIND_AUTHENTICATION);
-      bindDn = (String) options.get(BIND_DN);
-      bindCredential = (String) options.get(BIND_CREDENTIAL);
-      jaasSecurityDomain = (String) options.get(SECURITY_DOMAIN);
-
-      // User Search Settings
-      baseCtxDN = (String) options.get(BASE_CTX_DN);
-      baseFilter = (String) options.get(BASE_FILTER);
-
-      String temp = (String) options.get(SEARCH_TIME_LIMIT);
-      if (temp != null)
+      if (warned == false)
       {
-         try
-         {
-            searchTimeLimit = Integer.parseInt(temp);
-         }
-         catch (NumberFormatException e)
-         {
-            log.warn("Failed to parse: " + temp + ", using searchTimeLimit=" + searchTimeLimit);
-         }
+         warned = true;
+         String thisClass = this.getClass().getName();
+         String superClass = org.jboss.security.negotiation.AdvancedLdapLoginModule.class.getName();
+         log.warn("'" + thisClass + "' is deprecated, use '" + superClass + "' instead.");
       }
-
-      userSearchControls = new SearchControls();
-      userSearchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
-      userSearchControls.setReturningAttributes(new String[0]);
-      userSearchControls.setTimeLimit(searchTimeLimit);
-
-      rolesCtxDN = (String) options.get(ROLES_CTS_DN);
-      roleFilter = (String) options.get(ROLE_FILTER);
-
-      temp = (String) options.get(RECURSE_ROLES);
-      recurseRoles = Boolean.parseBoolean(temp);
-
-      int searchScope = SearchControls.SUBTREE_SCOPE;
-      temp = (String) options.get(ROLE_SEARCH_SCOPE);
-      if (OBJECT_SCOPE.equalsIgnoreCase(temp))
-      {
-         searchScope = SearchControls.OBJECT_SCOPE;
-      }
-      else if (ONELEVEL_SCOPE.equalsIgnoreCase(temp))
-      {
-         searchScope = SearchControls.ONELEVEL_SCOPE;
-      }
-      if (SUBTREE_SCOPE.equalsIgnoreCase(temp))
-      {
-         searchScope = SearchControls.SUBTREE_SCOPE;
-      }
-
-      roleSearchControls = new SearchControls();
-      roleSearchControls.setSearchScope(searchScope);
-      roleSearchControls.setReturningAttributes(new String[0]);
-      roleSearchControls.setTimeLimit(searchTimeLimit);
-
-      roleAttributeID = (String) options.get(ROLE_ATTRIBUTE_ID);
-
-      temp = (String) options.get(ROLE_ATTRIBUTE_IS_DN);
-      roleAttributeIsDN = Boolean.parseBoolean(temp);
-
-      roleNameAttributeID = (String) options.get(ROLE_NAME_ATTRIBUTE_ID);
-
-      temp = (String) options.get(ALLOW_EMPTY_PASSWORD);
-      allowEmptyPassword = Boolean.parseBoolean(temp);
-
    }
 
-   @Override
-   public boolean login() throws LoginException
-   {
-      Object result = null;
-
-      AuthorizeAction action = new AuthorizeAction();
-      if (AUTH_TYPE_GSSAPI.equals(bindAuthentication))
-      {
-         log.trace("Using GSSAPI to connect to LDAP");
-         LoginContext lc = new LoginContext(jaasSecurityDomain);
-         lc.login();
-         Subject serverSubject = lc.getSubject();
-
-         if (log.isDebugEnabled())
-         {
-            log.debug("Subject = " + serverSubject);
-            log.debug("Logged in '" + lc + "' LoginContext");
-         }
-
-         result = Subject.doAs(serverSubject, action);
-         lc.logout();
-      }
-      else
-      {
-         result = action.run();
-      }
-
-      if (result instanceof LoginException)
-      {
-         throw (LoginException) result;
-      }
-
-      return ((Boolean) result).booleanValue();
-   }
-
-   @Override
-   protected Principal getIdentity()
-   {
-      return identity;
-   }
-
-   @Override
-   protected Group[] getRoleSets() throws LoginException
-   {
-      Group[] roleSets =
-      {userRoles};
-      return roleSets;
-   }
-
-   protected Boolean innerLogin() throws LoginException
-   {
-      // Obtain the username and password
-      processIdentityAndCredential();
-      log.trace("Identity - " + getIdentity().getName());
-      // Initialise search ctx
-      String bindCredential = this.bindCredential;
-      if (AUTH_TYPE_GSSAPI.equals(bindAuthentication) == false)
-      {
-         if (jaasSecurityDomain != null && jaasSecurityDomain.length() > 0)
-         {
-            try
-            {
-               ObjectName serviceName = new ObjectName(jaasSecurityDomain);
-               char[] tmp = DecodeAction.decode(bindCredential, serviceName);
-               bindCredential = new String(tmp);
-            }
-            catch (Exception e)
-            {
-               LoginException le = new LoginException("Unabe to decode bindCredential");
-               le.initCause(e);
-               throw le;
-            }
-         }
-      }
-
-      LdapContext searchContext = null;
-
-      try
-      {
-         searchContext = constructLdapContext(bindDn, bindCredential, bindAuthentication);
-         log.debug("Obtained LdapContext");
-
-         // Search for user in LDAP
-         String userDN = findUserDN(searchContext);
-
-         // If authentication required authenticate as user
-         if (super.loginOk == false)
-         {
-            authenticate(userDN);
-         }
-
-         if (super.loginOk)
-         {
-            // Search for roles in LDAP
-            rolesSearch(searchContext, userDN);
-         }
-      }
-      finally
-      {
-         if (searchContext != null)
-         {
-            try
-            {
-               searchContext.close();
-            }
-            catch (NamingException e)
-            {
-               log.warn("Error closing context", e);
-            }
-         }
-      }
-
-      return Boolean.valueOf(super.loginOk);
-   }
-
-   /**
-    * Either retrieve existing values based on useFirstPass or use 
-    * CallBackHandler to obtain the values.
-    */
-   protected void processIdentityAndCredential() throws LoginException
-   {
-      if (super.login() == true)
-      {
-         Object username = sharedState.get("javax.security.auth.login.name");
-         if (username instanceof Principal)
-            identity = (Principal) username;
-         else
-         {
-            String name = username.toString();
-            try
-            {
-               identity = createIdentity(name);
-            }
-            catch (Exception e)
-            {
-               log.debug("Failed to create principal", e);
-               throw new LoginException("Failed to create principal: " + e.getMessage());
-            }
-         }
-         // We have no further use for a credential so no need to retrieve it.
-      }
-      else
-      {
-         try
-         {
-            NameCallback nc = new NameCallback("User name: ", "guest");
-            PasswordCallback pc = new PasswordCallback("Password: ", false);
-            Callback[] callbacks =
-            {nc, pc};
-
-            callbackHandler.handle(callbacks);
-            String username = nc.getName();
-            identity = createIdentity(username);
-            credential = pc.getPassword();
-            pc.clearPassword();
-         }
-         catch (Exception e)
-         {
-            LoginException le = new LoginException("Unable to obtain username/credential");
-            le.initCause(e);
-            throw le;
-         }
-
-      }
-   }
-
-   protected LdapContext constructLdapContext(String dn, Object credential, String authentication)
-         throws LoginException
-   {
-      Properties env = new Properties();
-      Iterator iter = options.entrySet().iterator();
-      while (iter.hasNext())
-      {
-         Entry entry = (Entry) iter.next();
-         env.put(entry.getKey(), entry.getValue());
-      }
-
-      // Set defaults for key values if they are missing
-      String factoryName = env.getProperty(Context.INITIAL_CONTEXT_FACTORY);
-      if (factoryName == null)
-      {
-         factoryName = DEFAULT_LDAP_CTX_FACTORY;
-         env.setProperty(Context.INITIAL_CONTEXT_FACTORY, factoryName);
-      }
-
-      // If this method is called with an authentication type then use that.
-      if (authentication != null && authentication.length() > 0)
-      {
-         env.setProperty(Context.SECURITY_AUTHENTICATION, authentication);
-      }
-      else
-      {
-         String authType = env.getProperty(Context.SECURITY_AUTHENTICATION);
-         if (authType == null)
-            env.setProperty(Context.SECURITY_AUTHENTICATION, AUTH_TYPE_SIMPLE);
-      }
-
-      String protocol = env.getProperty(Context.SECURITY_PROTOCOL);
-      String providerURL = (String) options.get(Context.PROVIDER_URL);
-      if (providerURL == null)
-      {
-         if (PROTOCOL_SSL.equals(protocol))
-         {
-            providerURL = DEFAULT_SSL_URL;
-         }
-         else
-         {
-            providerURL = DEFAULT_URL;
-         }
-         env.setProperty(Context.PROVIDER_URL, providerURL);
-      }
-
-      // Assume the caller of this method has checked the requirements for the principal and
-      // credentials.
-      if (dn != null)
-         env.setProperty(Context.SECURITY_PRINCIPAL, dn);
-      if (credential != null)
-         env.put(Context.SECURITY_CREDENTIALS, credential);
-      traceLdapEnv(env);
-      try
-      {
-         return new InitialLdapContext(env, null);
-      }
-      catch (NamingException e)
-      {
-         LoginException le = new LoginException("Unable to create new InitialLdapContext");
-         le.initCause(e);
-         throw le;
-      }
-   }
-
-   protected String findUserDN(LdapContext ctx) throws LoginException
-   {
-
-      if (baseCtxDN == null)
-      {
-         return getIdentity().getName();
-      }
-
-      try
-      {
-         NamingEnumeration results = null;
-
-         Object[] filterArgs =
-         {getIdentity().getName()};
-         results = ctx.search(baseCtxDN, baseFilter, filterArgs, userSearchControls);
-         if (results.hasMore() == false)
-         {
-            results.close();
-            throw new LoginException("Search of baseDN(" + baseCtxDN + ") found no matches");
-         }
-
-         SearchResult sr = (SearchResult) results.next();
-         String name = sr.getName();
-         String userDN = null;
-         if (sr.isRelative() == true)
-            userDN = name + "," + baseCtxDN;
-         else
-            throw new LoginException("Can't follow referal for authentication: " + name);
-
-         results.close();
-         results = null;
-
-         log.trace("findUserDN - " + userDN);
-         return userDN;
-      }
-      catch (NamingException e)
-      {
-         LoginException le = new LoginException("Unable to find user DN");
-         le.initCause(e);
-         throw le;
-      }
-   }
-
-   protected void authenticate(String userDN) throws LoginException
-   {
-      if (credential.length == 0)
-      {
-         if (allowEmptyPassword == false)
-         {
-            log.trace("Rejecting empty password.");
-            return;
-         }
-      }
-
-      try
-      {
-         LdapContext authContext = constructLdapContext(userDN, credential, null);
-         authContext.close();
-      }
-      catch (NamingException ne)
-      {
-         log.debug("Authentication failed - " + ne.getMessage());
-         LoginException le = new LoginException("Authentication failed");
-         le.initCause(ne);
-         throw le;
-      }
-
-      super.loginOk = true;
-      if (getUseFirstPass() == true)
-      { // Add the username and password to the shared state map
-         sharedState.put("javax.security.auth.login.name", getIdentity().getName());
-         sharedState.put("javax.security.auth.login.password", credential);
-      }
-
-   }
-
-   protected void rolesSearch(LdapContext searchContext, String dn) throws LoginException
-   {
-      Object[] filterArgs =
-      {getIdentity().getName(), dn};
-
-      NamingEnumeration results = null;
-      try
-      {
-         log.trace("rolesCtxDN=" + rolesCtxDN + " roleFilter=" + roleFilter + " filterArgs[0]=" + filterArgs[0]
-               + " filterArgs[1]=" + filterArgs[1]);
-
-         if (roleFilter != null && roleFilter.length() > 0)
-         {
-            results = searchContext.search(rolesCtxDN, roleFilter, filterArgs, roleSearchControls);
-            while (results.hasMore())
-            {
-               SearchResult sr = (SearchResult) results.next();
-               String resultDN = canonicalize(sr.getName());
-
-               obtainRole(searchContext, resultDN);
-            }
-         }
-         else
-         {
-            obtainRole(searchContext, dn);
-         }
-
-      }
-      catch (NamingException e)
-      {
-         LoginException le = new LoginException("Error finding roles");
-         le.initCause(e);
-         throw le;
-      }
-      finally
-      {
-         if (results != null)
-         {
-            try
-            {
-               results.close();
-            }
-            catch (NamingException e)
-            {
-               log.warn("Problem closing results", e);
-            }
-         }
-      }
-
-   }
-
-   protected void obtainRole(LdapContext searchContext, String dn) throws NamingException, LoginException
-   {
-      log.trace("rolesSearch resultDN = " + dn);
-
-      String[] attrNames =
-      {roleAttributeID};
-
-      Attributes result = searchContext.getAttributes(dn, attrNames);
-      if (result != null && result.size() > 0)
-      {
-         Attribute roles = result.get(roleAttributeID);
-         for (int n = 0; n < roles.size(); n++)
-         {
-            String roleName = (String) roles.get(n);
-            if (roleAttributeIsDN)
-            {
-               // Query the roleDN location for the value of roleNameAttributeID
-               String roleDN = roleName;
-               String[] returnAttribute =
-               {roleNameAttributeID};
-               log.trace("Using roleDN: " + roleDN);
-               try
-               {
-                  Attributes result2 = searchContext.getAttributes(roleDN, returnAttribute);
-                  Attribute roles2 = result2.get(roleNameAttributeID);
-                  if (roles2 != null)
-                  {
-                     for (int m = 0; m < roles2.size(); m++)
-                     {
-                        roleName = (String) roles2.get(m);
-                        addRole(roleName);
-                     }
-                  }
-               }
-               catch (NamingException e)
-               {
-                  log.trace("Failed to query roleNameAttrName", e);
-               }
-
-               if (recurseRoles)
-               {
-                  if (processedRoleDNs.contains(roleDN) == false)
-                  {
-                     processedRoleDNs.add(roleDN);
-                     log.trace("Recursive search for '" + roleDN + "'");
-                     rolesSearch(searchContext, roleDN);
-                  }
-                  else
-                  {
-                     log.trace("Already visited role '" + roleDN + "' ending recursion.");
-                  }
-               }
-            }
-            else
-            {
-               // The role attribute value is the role name
-               addRole(roleName);
-            }
-         }
-      }
-   }
-
-   protected void traceLdapEnv(Properties env)
-   {
-      if (log.isTraceEnabled())
-      {
-         Properties tmp = new Properties();
-         tmp.putAll(env);
-         String credentials = tmp.getProperty(Context.SECURITY_CREDENTIALS);
-         if (credentials != null && credentials.length() > 0)
-            tmp.setProperty(Context.SECURITY_CREDENTIALS, "***");
-         log.trace("Logging into LDAP server, env=" + tmp.toString());
-      }
-   }
-
-   private String canonicalize(String searchResult)
-   {
-      String result = searchResult;
-      int len = searchResult.length();
-
-      if (searchResult.endsWith("\""))
-      {
-         result = searchResult.substring(0, len - 1) + "," + rolesCtxDN + "\"";
-      }
-      else
-      {
-         result = searchResult + "," + rolesCtxDN;
-      }
-      return result;
-   }
-
-   private void addRole(String roleName)
-   {
-      if (roleName != null)
-      {
-         try
-         {
-            Principal p = super.createIdentity(roleName);
-            if (log.isTraceEnabled())
-               log.trace("Assign user '" + getIdentity().getName() + "' to role " + roleName);
-            userRoles.addMember(p);
-         }
-         catch (Exception e)
-         {
-            log.debug("Failed to create principal: " + roleName, e);
-         }
-      }
-   }
-
-   private class AuthorizeAction implements PrivilegedAction<Object>
-   {
-
-      public Object run()
-      {
-         try
-         {
-            return innerLogin();
-         }
-         catch (LoginException e)
-         {
-            return e;
-         }
-      }
-
-   }
-
 }

Modified: projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/NegotiationAuthenticator.java
===================================================================
--- projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/NegotiationAuthenticator.java	2008-10-07 22:24:51 UTC (rev 79236)
+++ projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/NegotiationAuthenticator.java	2008-10-07 22:30:51 UTC (rev 79237)
@@ -22,6 +22,8 @@
  */
 package org.jboss.security.negotiation;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.security.Principal;
 
@@ -32,7 +34,9 @@
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.deploy.LoginConfig;
 import org.apache.log4j.Logger;
+import org.jboss.security.negotiation.common.MessageTrace;
 import org.jboss.security.negotiation.common.NegotiationContext;
+import org.jboss.util.Base64;
 
 /**
  * An authenticator to manage Negotiation based authentication in connection with the
@@ -48,8 +52,6 @@
 
    private static final String NEGOTIATE = "Negotiate";
 
-   private static final String SPNEGO = "SPNEGO";
-
    private static final String NEGOTIATION_CONTEXT = "NEGOTIATION_CONTEXT";
 
    protected String getNegotiateScheme()
@@ -88,6 +90,12 @@
          throw new IOException("Invalid 'Authorization' header.");
       }
 
+      String authTokenBase64 = authHeader.substring(negotiateScheme.length() + 1);
+      byte[] authToken = Base64.decode(authTokenBase64);
+      ByteArrayInputStream authTokenIS = new ByteArrayInputStream(authToken);
+      MessageTrace.logRequestBase64(authTokenBase64);
+      MessageTrace.logRequestHex(authToken);
+
       Session session = request.getSessionInternal();
       NegotiationContext negotiationContext = (NegotiationContext) session.getNote(NEGOTIATION_CONTEXT);
       if (negotiationContext == null)
@@ -102,26 +110,46 @@
       // TODO - Probably not good if session reused.
       //        Maybe create arbitary ID or use SSO ID.
       String username = session.getId();
+      String authenticationMethod = "";
       try
       {
          // Set the ThreadLocal association.
          negotiationContext.associate();
-         negotiationContext.setRequestHeader(authHeader.substring(negotiateScheme.length() + 1));
 
+         MessageFactory mf = MessageFactory.newInstance();
+         if (mf.accepts(authTokenIS) == false)
+         {
+            throw new IOException("Unsupported negotiation mechanism.");
+         }
+
+         NegotiationMessage requestMessage = mf.createMessage(authTokenIS);
+         negotiationContext.setRequestMessage(requestMessage);
+
          Realm realm = context.getRealm();
-
          principal = realm.authenticate(username, (String) null);
+         authenticationMethod = negotiationContext.getAuthenticationMethod();
 
          if (log.isDebugEnabled())
             log.debug("authenticated principal = " + principal);
 
-         String responseHeader = negotiationContext.getResponseHeader();
+         NegotiationMessage responseMessage = negotiationContext.getResponseMessage();
+         ByteArrayOutputStream responseMessageOS = new ByteArrayOutputStream();
+         responseMessage.writeTo(responseMessageOS, true);
+         String responseHeader = responseMessageOS.toString();
+
+         MessageTrace.logResponseBase64(responseHeader);
+         // TODO - MessageTrace.logResponseHex()
+
          if (responseHeader != null)
          {
             response.setHeader("WWW-Authenticate", negotiateScheme + " " + responseHeader);
          }
 
       }
+      catch (NegotiationException e)
+      {
+         throw new IOException("Error processing " + negotiateScheme + " header.", e);
+      }
       finally
       {
          // Clear the headers and remove the ThreadLocal association.
@@ -134,10 +162,7 @@
       }
       else
       {
-         // TODO - Set the scheme based on what happened - the NegotiationContext
-         // is probably the correct vehicle for this as it is the result of the 
-         // negotiation that sets the outcome.
-         register(request, response, principal, SPNEGO, username, null);
+         register(request, response, principal, authenticationMethod, username, null);
       }
 
       return (principal != null);

Modified: projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/common/MessageTrace.java
===================================================================
--- projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/common/MessageTrace.java	2008-10-07 22:24:51 UTC (rev 79236)
+++ projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/common/MessageTrace.java	2008-10-07 22:30:51 UTC (rev 79237)
@@ -24,7 +24,6 @@
 
 import org.apache.log4j.Logger;
 
-
 /**
  * Handle message tracing hierarchy.
  * 

Modified: projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/common/NegotiationContext.java
===================================================================
--- projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/common/NegotiationContext.java	2008-10-07 22:24:51 UTC (rev 79236)
+++ projects/security/security-negotiation/trunk/jboss-negotiation-common/src/main/java/org/jboss/security/negotiation/common/NegotiationContext.java	2008-10-07 22:30:51 UTC (rev 79237)
@@ -23,6 +23,7 @@
 package org.jboss.security.negotiation.common;
 
 import org.apache.log4j.Logger;
+import org.jboss.security.negotiation.NegotiationMessage;
 
 /**
  * The NegotiationContext is the holder to contain the state of the current authentication 
@@ -37,25 +38,27 @@
 
    private static final Logger log = Logger.getLogger(NegotiationContext.class);
 
-   private static final ThreadLocal<NegotiationContext> spnegoContext = new ThreadLocal<NegotiationContext>();
+   private static final ThreadLocal<NegotiationContext> negotiationContext = new ThreadLocal<NegotiationContext>();
 
    private boolean authenticated = false;
 
-   private String requestHeader = null;
+   private String authenticationMethod;
 
-   private String responseHeader = null;
+   private NegotiationMessage requestMessage = null;
 
+   private NegotiationMessage responseMessage = null;
+
    private Object schemeContext = null;
 
-   public static NegotiationContext getCurrentSPNEGOContext()
+   public static NegotiationContext getCurrentNegotiationContext()
    {
-      return spnegoContext.get();
+      return negotiationContext.get();
    }
 
    public void associate()
    {
       log.trace("associate " + this.hashCode());
-      spnegoContext.set(this);
+      negotiationContext.set(this);
    }
 
    /**
@@ -64,9 +67,9 @@
    public void clear()
    {
       log.trace("clear " + this.hashCode());
-      requestHeader = null;
-      responseHeader = null;
-      spnegoContext.remove();
+      requestMessage = null;
+      responseMessage = null;
+      negotiationContext.remove();
    }
 
    public boolean isAuthenticated()
@@ -79,26 +82,36 @@
       this.authenticated = authenticated;
    }
 
-   public String getRequestHeader()
+   public String getAuthenticationMethod()
    {
-      return requestHeader;
+      return authenticationMethod;
    }
 
-   public void setRequestHeader(String requestHeader)
+   public void setAuthenticationMethod(String authenticationMethod)
    {
-      this.requestHeader = requestHeader;
+      this.authenticationMethod = authenticationMethod;
    }
 
-   public String getResponseHeader()
+   public NegotiationMessage getRequestMessage()
    {
-      return responseHeader;
+      return requestMessage;
    }
 
-   public void setResponseHeader(String responseHeader)
+   public void setRequestMessage(NegotiationMessage requestMessage)
    {
-      this.responseHeader = responseHeader;
+      this.requestMessage = requestMessage;
    }
 
+   public NegotiationMessage getResponseMessage()
+   {
+      return responseMessage;
+   }
+
+   public void setResponseMessage(NegotiationMessage responseMessage)
+   {
+      this.responseMessage = responseMessage;
+   }
+
    public Object getSchemeContext()
    {
       return schemeContext;

Modified: projects/security/security-negotiation/trunk/jboss-negotiation-spnego/src/main/java/org/jboss/security/negotiation/spnego/SPNEGOAuthenticator.java
===================================================================
--- projects/security/security-negotiation/trunk/jboss-negotiation-spnego/src/main/java/org/jboss/security/negotiation/spnego/SPNEGOAuthenticator.java	2008-10-07 22:24:51 UTC (rev 79236)
+++ projects/security/security-negotiation/trunk/jboss-negotiation-spnego/src/main/java/org/jboss/security/negotiation/spnego/SPNEGOAuthenticator.java	2008-10-07 22:30:51 UTC (rev 79237)
@@ -41,12 +41,19 @@
 
    private static final Logger log = Logger.getLogger(SPNEGOAuthenticator.class);
 
+   private static boolean warned = false;
+
    @Override
    public void setNext(Valve valve)
    {
-      String thisClass = this.getClass().getName();
-      String superClass = NegotiationAuthenticator.class.getName();
-      log.warn("'" + thisClass + "' is deprecated, use '" + superClass + "' instead.");
+      if (warned == false)
+      {
+         warned = true;
+         String thisClass = this.getClass().getName();
+         String superClass = NegotiationAuthenticator.class.getName();
+         log.warn("'" + thisClass + "' is deprecated, use '" + superClass + "' instead.");
+      }
+
       super.setNext(valve);
    }
 

Modified: projects/security/security-negotiation/trunk/jboss-negotiation-spnego/src/main/java/org/jboss/security/negotiation/spnego/SPNEGOLoginModule.java
===================================================================
--- projects/security/security-negotiation/trunk/jboss-negotiation-spnego/src/main/java/org/jboss/security/negotiation/spnego/SPNEGOLoginModule.java	2008-10-07 22:24:51 UTC (rev 79236)
+++ projects/security/security-negotiation/trunk/jboss-negotiation-spnego/src/main/java/org/jboss/security/negotiation/spnego/SPNEGOLoginModule.java	2008-10-07 22:30:51 UTC (rev 79237)
@@ -42,14 +42,11 @@
 import org.ietf.jgss.Oid;
 import org.jboss.security.SimpleGroup;
 import org.jboss.security.auth.spi.AbstractServerLoginModule;
-import org.jboss.security.negotiation.common.MessageTrace;
+import org.jboss.security.negotiation.NegotiationMessage;
 import org.jboss.security.negotiation.common.NegotiationContext;
 import org.jboss.security.negotiation.spnego.encoding.NegTokenInit;
-import org.jboss.security.negotiation.spnego.encoding.NegTokenInitDecoder;
 import org.jboss.security.negotiation.spnego.encoding.NegTokenTarg;
-import org.jboss.security.negotiation.spnego.encoding.NegTokenTargDecoder;
-import org.jboss.security.negotiation.spnego.encoding.NegTokenTargEncoder;
-import org.jboss.util.Base64;
+import org.jboss.security.negotiation.spnego.encoding.SPNEGOMessage;
 
 /**
  * Login module to work in conjunction with SPNEGOAuthenticator to handle the 
@@ -61,6 +58,8 @@
 public class SPNEGOLoginModule extends AbstractServerLoginModule
 {
 
+   private static final String SPNEGO = "SPNEGO";
+
    private static final Oid kerberos;
 
    // TODO - Pick a name for a default domain?
@@ -103,12 +102,12 @@
 
       super.loginOk = false;
 
-      NegotiationContext spnegoContext = NegotiationContext.getCurrentSPNEGOContext();
+      NegotiationContext negotiationContext = NegotiationContext.getCurrentNegotiationContext();
 
       try
       {
          Subject server = getServerSubject();
-         AcceptSecContext action = new AcceptSecContext(spnegoContext);
+         AcceptSecContext action = new AcceptSecContext(negotiationContext);
          Object result = Subject.doAs(server, action);
 
          log.trace("Result - " + result);
@@ -172,7 +171,7 @@
       Group callerPrincipal = new SimpleGroup("CallerPrincipal");
       Group[] groups =
       {roles, callerPrincipal};
-      callerPrincipal.addMember(identity);
+      callerPrincipal.addMember(getIdentity());
       return groups;
    }
 
@@ -193,28 +192,28 @@
    private class AcceptSecContext implements PrivilegedAction
    {
 
-      private final NegotiationContext spnegoContext;
+      private final NegotiationContext negotiationContext;
 
-      public AcceptSecContext(final NegotiationContext spnegoContext)
+      public AcceptSecContext(final NegotiationContext negotiationContext)
       {
-         this.spnegoContext = spnegoContext;
+         this.negotiationContext = negotiationContext;
       }
 
       public Object run()
       {
          try
          {
-            String requestHeader = spnegoContext.getRequestHeader();
-            byte[] reqToken = Base64.decode(requestHeader);
+            NegotiationMessage requestMessage = negotiationContext.getRequestMessage();
+            if (requestMessage instanceof SPNEGOMessage == false)
+            {
+               throw new LoginException("Unsupported negotiation mechanism.");
+            }
 
-            MessageTrace.logRequestBase64(spnegoContext.getRequestHeader());
-            MessageTrace.logRequestHex(reqToken);
+            // TODO - Ensure no way to fall through with gssToken still null.
             byte[] gssToken = null;
-
-            // TODO - If Section from MY Code!!
-            if (reqToken[0] == 0x60)
+            if (requestMessage instanceof NegTokenInit)
             {
-               NegTokenInit negTokenInit = NegTokenInitDecoder.decode(reqToken);
+               NegTokenInit negTokenInit = (NegTokenInit) requestMessage;
                List<Oid> mechList = negTokenInit.getMechTypes();
 
                if (mechList.get(0).equals(kerberos))
@@ -242,32 +241,20 @@
                   {
                      negTokenTarg.setNegResult(NegTokenTarg.REJECTED);
                   }
+                  negotiationContext.setResponseMessage(negTokenTarg);
 
-                  byte[] respSpnego = NegTokenTargEncoder.encode(negTokenTarg);
-                  String respEncoded = Base64.encodeBytes(respSpnego);
-
-                  MessageTrace.logResponseBase64(respEncoded);
-                  MessageTrace.logResponseHex(respSpnego);
-
-                  spnegoContext.setResponseHeader(respEncoded);
-
                   return Boolean.FALSE;
                }
 
             }
-            else if (reqToken[0] == (byte) 0xa1)
+            else if (requestMessage instanceof NegTokenTarg)
             {
-               NegTokenTarg negTokenTarg = NegTokenTargDecoder.decode(reqToken);
+               NegTokenTarg negTokenTarg = (NegTokenTarg) requestMessage;
 
                gssToken = negTokenTarg.getResponseToken();
             }
-            else
-            {
-               // TODO - Detect NTLM to specific error can be reported.
-               throw new LoginException("Unsupported negotiation mechanism.");
-            }
 
-            Object schemeContext = spnegoContext.getSchemeContext();
+            Object schemeContext = negotiationContext.getSchemeContext();
             if (schemeContext != null && schemeContext instanceof GSSContext == false)
             {
                throw new IllegalStateException("The schemeContext is not a GSSContext");
@@ -280,19 +267,22 @@
                GSSManager manager = GSSManager.getInstance();
                gssContext = manager.createContext((GSSCredential) null);
 
-               spnegoContext.setSchemeContext(gssContext);
+               negotiationContext.setSchemeContext(gssContext);
             }
 
             if (gssContext.isEstablished())
             {
                log.warn("Authentication was performed despite already being authenticated!");
+
+               // TODO - Refactor to only do this once.
                identity = new KerberosPrincipal(gssContext.getSrcName().toString());
 
                log.debug("context.getCredDelegState() = " + gssContext.getCredDelegState());
                log.debug("context.getMutualAuthState() = " + gssContext.getMutualAuthState());
                log.debug("context.getSrcName() = " + gssContext.getSrcName().toString());
 
-               spnegoContext.setAuthenticated(true);
+               negotiationContext.setAuthenticationMethod(SPNEGO);
+               negotiationContext.setAuthenticated(true);
 
                return Boolean.TRUE;
             }
@@ -304,13 +294,7 @@
                NegTokenTarg negTokenTarg = new NegTokenTarg();
                negTokenTarg.setResponseToken(respToken);
 
-               byte[] respSpnego = NegTokenTargEncoder.encode(negTokenTarg);
-               String respEncoded = Base64.encodeBytes(respSpnego);
-
-               MessageTrace.logResponseBase64(respEncoded);
-               MessageTrace.logResponseHex(respSpnego);
-
-               spnegoContext.setResponseHeader(respEncoded);
+               negotiationContext.setResponseMessage(negTokenTarg);
             }
 
             if (gssContext.isEstablished() == false)
@@ -325,7 +309,9 @@
                log.debug("context.getMutualAuthState() = " + gssContext.getMutualAuthState());
                log.debug("context.getSrcName() = " + gssContext.getSrcName().toString());
 
-               spnegoContext.setAuthenticated(true);
+               // TODO - Get these two in synch - maybe isAuthenticated based on an authentication method been set?
+               negotiationContext.setAuthenticationMethod(SPNEGO);
+               negotiationContext.setAuthenticated(true);
                return Boolean.TRUE;
             }
 

Modified: projects/security/security-negotiation/trunk/jboss-negotiation-toolkit/src/main/java/org/jboss/security/negotiation/toolkit/SecuredServlet.java
===================================================================
--- projects/security/security-negotiation/trunk/jboss-negotiation-toolkit/src/main/java/org/jboss/security/negotiation/toolkit/SecuredServlet.java	2008-10-07 22:24:51 UTC (rev 79236)
+++ projects/security/security-negotiation/trunk/jboss-negotiation-toolkit/src/main/java/org/jboss/security/negotiation/toolkit/SecuredServlet.java	2008-10-07 22:30:51 UTC (rev 79237)
@@ -34,7 +34,6 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.log4j.Logger;
 import org.jboss.security.SecurityAssociation;
 
 /**
@@ -49,14 +48,10 @@
 
    private static final long serialVersionUID = 4708999345009728352L;
 
-   private static final Logger log = Logger.getLogger(SecuredServlet.class);
-
    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException,
          IOException
    {
-      log.info(String.valueOf(req.getUserPrincipal()));
-
       PrintWriter writer = resp.getWriter();
 
       writer.println("<html>");
@@ -67,6 +62,9 @@
       writer.println("    <h1>Negotiation Toolkit</h1>");
       writer.println("    <h2>Secured</h2>");
 
+      writer.println("    <h5>Auth Type</h5>");
+      writeObject(req.getAuthType(), writer);
+      
       writer.println("    <h5>User Principal</h5>");
       writeObject(req.getUserPrincipal(), writer);
 




More information about the jboss-cvs-commits mailing list