[jboss-cvs] Picketbox SVN: r280 - in trunk/security-jboss-sx/jbosssx/src: test/java/org/jboss/test/authentication/cbh and 1 other directory.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Wed Nov 2 11:23:00 EDT 2011
Author: anil.saldhana at jboss.com
Date: 2011-11-02 11:23:00 -0400 (Wed, 02 Nov 2011)
New Revision: 280
Added:
trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DecodeAction.java
trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/LdapCallbackHandler.java
trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/VerifyPasswordCallback.java
trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/cbh/LdapCallbackHandlerUnitTestCase.java
Modified:
trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DatabaseCallbackHandler.java
trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/cbh/DatabaseCallbackHandlerUnitTestCase.java
Log:
SECURITY-467: SECURITY-470: db and ldap callback handlers
Modified: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DatabaseCallbackHandler.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DatabaseCallbackHandler.java 2011-11-01 20:48:30 UTC (rev 279)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DatabaseCallbackHandler.java 2011-11-02 15:23:00 UTC (rev 280)
@@ -36,6 +36,7 @@
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.CredentialException;
import javax.sql.DataSource;
import org.jboss.logging.Logger;
@@ -255,13 +256,21 @@
* Handle a {@code Callback}
* @param c callback
* @throws UnsupportedCallbackException If the callback is not supported by this handler
+ * @throws IOException
*/
- protected void handleCallBack( Callback c ) throws UnsupportedCallbackException
+ protected void handleCallBack( Callback c ) throws UnsupportedCallbackException, IOException
{
if(c instanceof VerifyPasswordCallback)
{
VerifyPasswordCallback vpc = (VerifyPasswordCallback) c;
- handleVerification(vpc);
+ try
+ {
+ handleVerification(vpc);
+ }
+ catch (CredentialException e)
+ {
+ throw new IOException(e);
+ }
}
if(c instanceof PasswordCallback == false)
return;
@@ -271,7 +280,7 @@
passwdCallback.setPassword(getPassword().toCharArray());
}
- protected void handleVerification(VerifyPasswordCallback vpc)
+ protected void handleVerification(VerifyPasswordCallback vpc) throws CredentialException
{
String userPass = vpc.getValue();
String passwordFromDB = getPassword();
@@ -279,6 +288,10 @@
{
vpc.setVerified(true);
}
+ else
+ {
+ throw new CredentialException(ErrorCodes.ACCESS_DENIED + "Passwords don't match");
+ }
}
private String getPassword()
Added: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DecodeAction.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DecodeAction.java (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DecodeAction.java 2011-11-02 15:23:00 UTC (rev 280)
@@ -0,0 +1,100 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.security.auth.callback;
+
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.jboss.security.util.MBeanServerLocator;
+
+/**
+ * PriviledgedActions used by login modules for decoding passwords
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 2 $
+ */
+class DecodeAction implements PrivilegedExceptionAction<Object>
+{
+ /** The permission required to access decode, decode64 */
+ private static final RuntimePermission decodePermission =
+ new RuntimePermission("org.jboss.security.auth.spi.DecodeAction.decode");
+
+ String password;
+ ObjectName serviceName;
+
+ DecodeAction(String password, ObjectName serviceName)
+ {
+ this.password = password;
+ this.serviceName = serviceName;
+ }
+
+ /**
+ *
+ * @return
+ * @throws Exception
+ */
+ public Object run() throws Exception
+ {
+ // Invoke the decodeb64 op
+ byte[] secret = decode64(password);
+ // Convert to UTF-8 base char array
+ String secretPassword = new String(secret, "UTF-8");
+ return secretPassword.toCharArray();
+ }
+
+ /** Decrypt the secret using the cipherKey.
+ *
+ * @param secret - the encrypted secret to decrypt.
+ * @return the decrypted secret
+ * @throws Exception
+ */
+ private byte[] decode64(String secret)
+ throws Exception
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if( sm != null )
+ sm.checkPermission(decodePermission);
+
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ return (byte[]) server.invoke(serviceName, "decode64", new Object[] {secret},
+ new String[] {String.class.getName()});
+ }
+
+ static char[] decode(String password, ObjectName serviceName)
+ throws Exception
+ {
+ DecodeAction action = new DecodeAction(password, serviceName);
+ try
+ {
+ char[] decode = (char[]) AccessController.doPrivileged(action);
+ return decode;
+ }
+ catch(PrivilegedActionException e)
+ {
+ throw e.getException();
+ }
+ }
+}
Property changes on: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/DecodeAction.java
___________________________________________________________________
Added: svn:executable
+ *
Added: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/LdapCallbackHandler.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/LdapCallbackHandler.java (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/LdapCallbackHandler.java 2011-11-02 15:23:00 UTC (rev 280)
@@ -0,0 +1,534 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2011, 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.auth.callback;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+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.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.jboss.logging.Logger;
+import org.jboss.security.ErrorCodes;
+
+/**
+ * <p>
+ * A {@code CallbackHandler} using the LDAP to match the passed password.
+ * </p>
+ * <p>
+ * There are two callbacks that can be passed to this handler.
+ * <ol>
+ * <li>{@code PasswordCallback}: Passing this callback will get the password for the user.
+ * The returned password will not be in clear text. It will
+ * be in the hashed form the ldap server has stored.
+ * </li>
+ * <li>{@code VerifyPasswordCallback} Passing this callback with a value will make the handler
+ * to do a ldap bind to verify the user password.
+ * </li>
+ * </ol>
+ * </p>
+ * <p>
+ * The main method is {@code #setConfiguration(Map)} which takes in a map of String key/value pairs.
+ * The possible pairs are:
+ * <ol>
+ * <li>passwordAttributeID : what is the name of the attribute where the password is stored. Default: userPassword</li>
+ * <li>bindDN : DN used to bind against the ldap server with read/write permissions for baseCtxDN.</li>
+ * <li>bindCredential : Password for the bindDN. This can be encrypted if the jaasSecurityDomain is specified.</li>
+ * <li>baseCtxDN : The fixed DN of the context to start the user search from.</li>
+ * <li>baseFilter: A search filter used to locate the context of the user to authenticate.
+ * The input username/userDN as provided by the {@code NameCallback}
+ * will be substituted into the filter anywhere a "{0}" expression is seen.
+ * This substitution behavior comes from the standard.</li>
+ * <li>searchTimeLimit : The timeout in milliseconds for the user/role searches. Defaults to 10000 (10 seconds).</li>
+ * <li>jaasSecurityDomain : The JMX ObjectName of the JaasSecurityDomain to use to decrypt the java.naming.security.principal.
+ * The encrypted form of the password is that returned by the JaasSecurityDomain#encrypt64(byte[]) method.
+ * The org.jboss.security.plugins.PBEUtils can also be used to generate the encrypted form.</li>
+ * <li>distinguishedNameAttribute : Used in ldap servers such as Active Directory where the ldap provider has a property (distinguishedName)
+ * to return the relative CN of the user. Default: distinguishedName</li>
+ * </ol>
+ * </p>
+ * <p>
+ * Example Usages:
+ * <pre>
+ * LdapCallbackHandler cbh = new LdapCallbackHandler();
+ * Map<String,String> map = new HashMap<String,String>();
+ * map.put("bindDN", "cn=Directory Manager");
+ * map.put("bindCredential", "password");
+ * map.put("baseFilter", "(uid={0})");
+ * map.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
+ * map.put("java.naming.provider.url", "ldap://localhost:10389");
+ * map.put("baseCtxDN", "ou=People,dc=jboss,dc=org");
+ * cbh.setConfiguration(map);
+ * NameCallback ncb = new NameCallback("Enter");
+ * ncb.setName("jduke");
+ * VerifyPasswordCallback vpc = new VerifyPasswordCallback();
+ * vpc.setValue("theduke");
+ * cbh.handle(new Callback[] {ncb,vpc} );
+ * assertTrue(vpc.isVerified());
+ * </pre>
+ * </p>
+ * @author Anil Saldhana
+ * @since Nov 1, 2011
+ */
+public class LdapCallbackHandler extends AbstractCallbackHandler implements CallbackHandler
+{
+ protected static Logger log = Logger.getLogger(LdapCallbackHandler.class);
+ protected boolean trace = log.isTraceEnabled();
+
+ private static final String PASSWORD_ATTRIBUTE_ID = "passwordAttributeID";
+
+ private static final String BIND_DN = "bindDN";
+
+ private static final String BIND_CREDENTIAL = "bindCredential";
+
+ private static final String BASE_CTX_DN = "baseCtxDN";
+
+ private static final String BASE_FILTER_OPT = "baseFilter";
+
+ private static final String SEARCH_TIME_LIMIT_OPT = "searchTimeLimit";
+
+ private static final String SECURITY_DOMAIN_OPT = "jaasSecurityDomain";
+
+ private static final String DISTINGUISHED_NAME_ATTRIBUTE_OPT = "distinguishedNameAttribute";
+
+ protected String bindDN;
+
+ protected String bindCredential;
+
+ protected String baseDN;
+
+ protected String baseFilter;
+
+ protected String passwordAttributeID = "userPassword";
+
+ protected int recursion = 0;
+
+ protected int searchTimeLimit = 10000;
+
+ protected int searchScope = SearchControls.SUBTREE_SCOPE;
+
+ protected String distinguishedNameAttribute;
+
+ protected boolean parseUsername;
+
+ protected String usernameBeginString;
+
+ protected String usernameEndString;
+
+ // simple flag to indicate is the validatePassword method was called
+ protected boolean isPasswordValidated = false;
+
+ protected Map<String,String> options = new HashMap<String, String>();
+
+ public LdapCallbackHandler()
+ {
+ }
+
+ public void setConfiguration(Map<String,String> config)
+ {
+ if(config != null)
+ {
+ options.putAll(config);
+ }
+ }
+
+ public void handle(Callback[] callbacks) throws IOException,
+ UnsupportedCallbackException
+ {
+ if(userName == null)
+ {
+ userName = getUserName(callbacks);
+ }
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ Callback callback = callbacks[i];
+ try
+ {
+ this.handleCallBack( callback );
+ }
+ catch (NamingException e)
+ {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ /**
+ * Handle a {@code Callback}
+ * @param c callback
+ * @throws UnsupportedCallbackException If the callback is not supported by this handler
+ * @throws NamingException
+ */
+ protected void handleCallBack( Callback c ) throws UnsupportedCallbackException, NamingException
+ {
+ if(c instanceof VerifyPasswordCallback)
+ {
+ verifyPassword((VerifyPasswordCallback) c);
+ return;
+ }
+
+ if(c instanceof PasswordCallback == false)
+ return;
+
+ PasswordCallback passwdCallback = (PasswordCallback) c;
+
+ String bindDN = getBindDN();
+
+ String bindCredential = getBindCredential();
+
+ String tmp = options.get(PASSWORD_ATTRIBUTE_ID);
+ if(tmp != null && tmp.length() > 0)
+ {
+ passwordAttributeID = tmp;
+ }
+
+ InitialLdapContext ctx;
+ ClassLoader currentTCCL = SecurityActions.getContextClassLoader();
+ try
+ {
+ if (currentTCCL != null)
+ SecurityActions.setContextClassLoader(null);
+ ctx = this.constructInitialLdapContext(bindDN, bindCredential);
+ }
+ catch (NamingException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ String timeLimit = (String) options.get(SEARCH_TIME_LIMIT_OPT);
+ if (timeLimit != null)
+ {
+ try
+ {
+ searchTimeLimit = Integer.parseInt(timeLimit);
+ }
+ catch (NumberFormatException e)
+ {
+ if (trace)
+ log.trace("Failed to parse: " + timeLimit + ", using searchTimeLimit=" + searchTimeLimit, e);
+ }
+ }
+ if(searchTimeLimit == 0)
+ searchTimeLimit = 10000;
+
+ String baseDN = (String) options.get(BASE_CTX_DN);
+ String baseFilter = (String) options.get(BASE_FILTER_OPT);
+
+ SearchControls constraints = new SearchControls();
+ constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+ constraints.setTimeLimit(searchTimeLimit);
+
+
+ NamingEnumeration<SearchResult> results = null;
+
+ Object[] filterArgs = {userName};
+ try
+ {
+ if(baseDN == null)
+ throw new NamingException(ErrorCodes.NULL_VALUE + BASE_CTX_DN + " is null");
+ results = ctx.search(baseDN, baseFilter, filterArgs, constraints);
+ if (results.hasMore() == false)
+ {
+ safeClose(results);
+ throw new NamingException(ErrorCodes.PROCESSING_FAILED + "Search of baseDN(" + baseDN + ") found no matches");
+ }
+ SearchResult sr = results.next();
+ String name = sr.getName();
+ String userDN = null;
+ if (sr.isRelative() == true)
+ userDN = name + "," + baseDN;
+ else
+ throw new NamingException(ErrorCodes.PROCESSING_FAILED + "Can't follow referal for authentication: " + name);
+
+ safeClose(results);
+
+ //Finished Authentication. Lets look for the attributes
+ filterArgs = new Object[]{userName, userDN};
+ results = ctx.search(userDN, baseFilter, filterArgs, constraints);
+ try
+ {
+ while (results.hasMore())
+ {
+ sr = (SearchResult) results.next();
+ Attributes attributes = sr.getAttributes();
+ NamingEnumeration<? extends javax.naming.directory.Attribute> ne = attributes.getAll();
+
+ while(ne != null && ne.hasMoreElements())
+ {
+ javax.naming.directory.Attribute ldapAtt = ne.next();
+ if(passwordAttributeID.equalsIgnoreCase(ldapAtt.getID()))
+ {
+ Object thePass = ldapAtt.get();
+ setPasswordCallbackValue(thePass, passwdCallback);
+ }
+ }
+ }
+ }
+ finally
+ {
+ safeClose(results);
+ safeClose(ctx);
+ if (currentTCCL != null)
+ SecurityActions.setContextClassLoader(currentTCCL);
+ }
+ }
+ catch(NamingException ne)
+ {
+ log.error(ne);
+ return;
+ }
+ results = null;
+ }
+
+ protected void verifyPassword( VerifyPasswordCallback vpc) throws NamingException
+ {
+ String credential = vpc.getValue();
+
+ ClassLoader currentTCCL = SecurityActions.getContextClassLoader();
+ if (currentTCCL != null)
+ SecurityActions.setContextClassLoader(null);
+
+ String baseDN = (String) options.get(BASE_CTX_DN);
+ String baseFilter = (String) options.get(BASE_FILTER_OPT);
+
+ InitialLdapContext ctx= this.constructInitialLdapContext(bindDN, bindCredential);
+ bindDNAuthentication(ctx, userName, credential, baseDN, baseFilter);
+ vpc.setVerified(true);
+ }
+
+ protected String getBindDN()
+ {
+ String bindDN = (String) options.get(BIND_DN);
+ if(bindDN == null || bindDN.length() == 0)
+ {
+ if(trace)
+ log.trace("bindDN is not found");
+ }
+ return bindDN;
+ }
+
+ protected String getBindCredential()
+ {
+ String bindCredential = (String) options.get(BIND_CREDENTIAL);
+ if (bindCredential.startsWith("{EXT}"))
+ {
+ try
+ {
+ bindCredential = new String(org.jboss.security.Util.loadPassword(bindCredential));
+ }
+ catch (Exception e1)
+ {
+ log.error("Exception in decrypting bindCredential:",e1);
+ }
+ }
+ String securityDomain = (String) options.get(SECURITY_DOMAIN_OPT);
+ if (securityDomain != null)
+ {
+ try
+ {
+ ObjectName serviceName = new ObjectName(securityDomain);
+ char[] tmp = DecodeAction.decode(bindCredential, serviceName);
+ bindCredential = new String(tmp);
+ }
+ catch (Exception e)
+ {
+ log.error("Exception in decrypting bindCredential:",e);
+ }
+ }
+ return bindCredential;
+ }
+
+ protected void setPasswordCallbackValue(Object thePass, PasswordCallback passwdCallback)
+ {
+ String tmp = null;
+ if(thePass instanceof String)
+ {
+ tmp = (String) thePass;
+ passwdCallback.setPassword(tmp.toCharArray());
+ }
+ else if(thePass instanceof char[])
+ {
+ passwdCallback.setPassword((char[])thePass);
+ }
+ else if(thePass instanceof byte[])
+ {
+ byte[] theBytes = (byte[]) thePass;
+ passwdCallback.setPassword((new String(theBytes).toCharArray()));
+ }
+ else
+ {
+ throw new RuntimeException(ErrorCodes.WRONG_TYPE + "password type:" + thePass.getClass());
+ }
+ }
+
+ private InitialLdapContext constructInitialLdapContext(String dn, Object credential) throws NamingException
+ {
+ Properties env = new Properties();
+ Iterator<Entry<String, String>> iter = options.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ Entry<String, String> 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 = "com.sun.jndi.ldap.LdapCtxFactory";
+ env.setProperty(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ }
+ String authType = env.getProperty(Context.SECURITY_AUTHENTICATION);
+ if (authType == null)
+ env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ String protocol = env.getProperty(Context.SECURITY_PROTOCOL);
+ String providerURL = (String) options.get(Context.PROVIDER_URL);
+ if (providerURL == null)
+ providerURL = "ldap://localhost:" + ((protocol != null && protocol.equals("ssl")) ? "636" : "389");
+
+ env.setProperty(Context.PROVIDER_URL, providerURL);
+
+ distinguishedNameAttribute = (String) options.get(DISTINGUISHED_NAME_ATTRIBUTE_OPT);
+ if (distinguishedNameAttribute == null)
+ distinguishedNameAttribute = "distinguishedName";
+
+
+ // JBAS-3555, allow anonymous login with no bindDN and bindCredential
+ if (dn != null)
+ env.setProperty(Context.SECURITY_PRINCIPAL, dn);
+ if (credential != null)
+ env.put(Context.SECURITY_CREDENTIALS, credential);
+ traceLdapEnv(env);
+ return new InitialLdapContext(env, null);
+ }
+
+ /**
+ @param ctx - the context to search from
+ @param user - the input username
+ @param credential - the bind credential
+ @param baseDN - base DN to search the ctx from
+ @param filter - the search filter string
+ @return the userDN string for the successful authentication
+ @throws NamingException
+ */
+ @SuppressWarnings("rawtypes")
+ protected String bindDNAuthentication(InitialLdapContext ctx, String user, Object credential, String baseDN,
+ String filter) throws NamingException
+ {
+ SearchControls constraints = new SearchControls();
+ constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ constraints.setTimeLimit(searchTimeLimit);
+ String attrList[] = {distinguishedNameAttribute};
+ constraints.setReturningAttributes(attrList);
+
+ NamingEnumeration results = null;
+
+ Object[] filterArgs = {user};
+ results = ctx.search(baseDN, filter, filterArgs, constraints);
+ if (results.hasMore() == false)
+ {
+ results.close();
+ throw new NamingException(ErrorCodes.PROCESSING_FAILED + "Search of baseDN(" + baseDN + ") found no matches");
+ }
+
+ SearchResult sr = (SearchResult) results.next();
+ String name = sr.getName();
+ String userDN = null;
+ Attributes attrs = sr.getAttributes();
+ if (attrs != null)
+ {
+ Attribute dn = attrs.get(distinguishedNameAttribute);
+ if (dn != null)
+ {
+ userDN = (String) dn.get();
+ }
+ }
+ if (userDN == null)
+ {
+ if (sr.isRelative() == true)
+ userDN = name + ("".equals(baseDN) ? "" : "," + baseDN);
+ else
+ throw new NamingException(ErrorCodes.PROCESSING_FAILED + "Can't follow referal for authentication: " + name);
+ }
+
+ safeClose(results);
+ results = null;
+
+ InitialLdapContext userCtx = constructInitialLdapContext(userDN, credential);
+ safeClose(userCtx);
+
+ return userDN;
+ }
+
+ private void traceLdapEnv(Properties env)
+ {
+ if (trace)
+ {
+ Properties tmp = new Properties();
+ tmp.putAll(env);
+ tmp.setProperty(Context.SECURITY_CREDENTIALS, "***");
+ log.trace("Logging into LDAP server, env=" + tmp.toString());
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected void safeClose(NamingEnumeration results)
+ {
+ if(results != null)
+ {
+ try
+ {
+ results.close();
+ } catch (NamingException e) {}
+ }
+ }
+
+ protected void safeClose(InitialLdapContext ic)
+ {
+ if(ic != null)
+ {
+ try
+ {
+ ic.close();
+ }
+ catch (NamingException e)
+ {
+ }
+ }
+ }
+}
\ No newline at end of file
Added: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/VerifyPasswordCallback.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/VerifyPasswordCallback.java (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/VerifyPasswordCallback.java 2011-11-02 15:23:00 UTC (rev 280)
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2011, 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.auth.callback;
+
+import javax.security.auth.callback.Callback;
+
+/**
+ * A {@code Callback} that indicates that the enclosing
+ * value needs to be checked against the value stored in
+ * the data store such as a DB or LDAP
+ * @author Anil Saldhana
+ * @since Nov 1, 2011
+ */
+public class VerifyPasswordCallback implements Callback
+{
+ protected String value;
+
+ protected boolean verified = false;
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ public void setValue(char[] value)
+ {
+ this.value = new String(value);
+ }
+
+ public void setValue(byte[] value)
+ {
+ this.value = new String(value);
+ }
+
+ public boolean isVerified() {
+ return verified;
+ }
+
+ public void setVerified(boolean verified) {
+ this.verified = verified;
+ }
+}
\ No newline at end of file
Modified: trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/cbh/DatabaseCallbackHandlerUnitTestCase.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/cbh/DatabaseCallbackHandlerUnitTestCase.java 2011-11-01 20:48:30 UTC (rev 279)
+++ trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/cbh/DatabaseCallbackHandlerUnitTestCase.java 2011-11-02 15:23:00 UTC (rev 280)
@@ -24,7 +24,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
@@ -36,10 +38,12 @@
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
-
+import javax.security.auth.login.CredentialException;
+
import org.jboss.security.auth.callback.DatabaseCallbackHandler;
+import org.jboss.security.auth.callback.VerifyPasswordCallback;
import org.junit.Before;
-import org.junit.Test;
+import org.junit.Test;
/**
* Unit test the {@code DatabaseCallbackHandler}
@@ -126,6 +130,61 @@
assertNotSame("anilpass", new String(pcb.getPassword()));
}
+ @Test
+ public void testVerifyPasswordCallbackPass() throws Exception
+ {
+ query();
+ DatabaseCallbackHandler cbh = new DatabaseCallbackHandler();
+
+ Map<String,String> map = new HashMap<String,String>();
+ map.put(DatabaseCallbackHandler.DB_DRIVERNAME, driverName);
+ map.put(DatabaseCallbackHandler.CONNECTION_URL, connectionURL);
+ map.put(DatabaseCallbackHandler.DB_USERNAME, "sa");
+ map.put(DatabaseCallbackHandler.DB_USERPASS, "");
+
+ cbh.setConfiguration(map);
+
+ NameCallback ncb = new NameCallback("Enter");
+ ncb.setName("anil");
+
+ VerifyPasswordCallback vpc = new VerifyPasswordCallback();
+ vpc.setValue("anilpass");
+
+ cbh.handle(new Callback[] {ncb,vpc} );
+ }
+
+ @Test
+ public void testVerifyPasswordCallbackFail() throws Exception
+ {
+ query();
+ DatabaseCallbackHandler cbh = new DatabaseCallbackHandler();
+
+ Map<String,String> map = new HashMap<String,String>();
+ map.put(DatabaseCallbackHandler.DB_DRIVERNAME, driverName);
+ map.put(DatabaseCallbackHandler.CONNECTION_URL, connectionURL);
+ map.put(DatabaseCallbackHandler.DB_USERNAME, "sa");
+ map.put(DatabaseCallbackHandler.DB_USERPASS, "");
+
+ cbh.setConfiguration(map);
+
+ NameCallback ncb = new NameCallback("Enter");
+ ncb.setName("anil");
+
+ VerifyPasswordCallback vpc = new VerifyPasswordCallback();
+ vpc.setValue("bad");
+
+ try
+ {
+ cbh.handle(new Callback[] {ncb,vpc} );
+ fail("no exception thrown");
+ }
+ catch(IOException ie)
+ {
+ Throwable cause = ie.getCause();
+ assertTrue( cause instanceof CredentialException);
+ }
+ }
+
private void query() throws Exception
{
Connection conn = getConnection();
Added: trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/cbh/LdapCallbackHandlerUnitTestCase.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/cbh/LdapCallbackHandlerUnitTestCase.java (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/cbh/LdapCallbackHandlerUnitTestCase.java 2011-11-02 15:23:00 UTC (rev 280)
@@ -0,0 +1,121 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2011, 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.test.authentication.cbh;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.AuthenticationException;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+
+import org.jboss.security.auth.callback.LdapCallbackHandler;
+import org.jboss.security.auth.callback.VerifyPasswordCallback;
+import org.jboss.test.security.ldap.OpenDSUnitTestsAdapter;
+import org.junit.Test;
+
+/**
+ * Unit test the {@code LdapCallbackHandler}
+ * @author Anil Saldhana
+ * @since Oct 31, 2011
+ */
+public class LdapCallbackHandlerUnitTestCase extends OpenDSUnitTestsAdapter
+{
+ public LdapCallbackHandlerUnitTestCase(String name)
+ {
+ super(name);
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ //Let us add the ldapAttributes.ldif
+ String fileName = targetDir + "ldap" + fs + "ldapAttributes.ldif";
+ boolean op = util.addLDIF(serverHost, port, adminDN, adminPW, new File(fileName).toURI().toURL());
+ assertTrue(op);
+ }
+
+ @Test
+ public void testSuccessfulCBH() throws Exception
+ {
+ LdapCallbackHandler cbh = new LdapCallbackHandler();
+
+ Map<String,String> map = new HashMap<String,String>();
+ map.put("bindDN", "cn=Directory Manager");
+ map.put("bindCredential", "password");
+ map.put("baseFilter", "(uid={0})");
+ map.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
+ map.put("java.naming.provider.url", "ldap://localhost:10389");
+ map.put("baseCtxDN", "ou=People,dc=jboss,dc=org");
+
+ cbh.setConfiguration(map);
+
+ NameCallback ncb = new NameCallback("Enter");
+ ncb.setName("jduke");
+
+ VerifyPasswordCallback vpc = new VerifyPasswordCallback();
+ vpc.setValue("theduke");
+
+ cbh.handle(new Callback[] {ncb,vpc} );
+
+ assertTrue(vpc.isVerified());
+ }
+
+ @Test
+ public void testFailCBH() throws Exception
+ {
+ LdapCallbackHandler cbh = new LdapCallbackHandler();
+
+ Map<String,String> map = new HashMap<String,String>();
+ map.put("bindDN", "cn=Directory Manager");
+ map.put("bindCredential", "password");
+ map.put("baseFilter", "(uid={0})");
+ map.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
+ map.put("java.naming.provider.url", "ldap://localhost:10389");
+ map.put("baseCtxDN", "ou=People,dc=jboss,dc=org");
+
+ cbh.setConfiguration(map);
+
+ NameCallback ncb = new NameCallback("Enter");
+ ncb.setName("jduke");
+
+ VerifyPasswordCallback vpc = new VerifyPasswordCallback();
+ vpc.setValue("badDUDE");
+
+ try
+ {
+ cbh.handle(new Callback[] {ncb,vpc} );
+ fail("should have thrown ex");
+ }
+ catch(IOException ae)
+ {
+ Throwable cause = ae.getCause();
+ assertNotNull(cause);
+ assertTrue(cause instanceof AuthenticationException);
+ }
+
+ assertFalse(vpc.isVerified());
+ }
+}
\ No newline at end of file
More information about the jboss-cvs-commits
mailing list