[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