You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2006/07/17 21:21:14 UTC
svn commit: r422808 - in /directory/branches/apacheds/optimization:
core-unit/src/test/java/org/apache/directory/server/core/authn/
core/src/main/java/org/apache/directory/server/core/authn/
Author: akarasulu
Date: Mon Jul 17 12:21:14 2006
New Revision: 422808
URL: http://svn.apache.org/viewvc?rev=422808&view=rev
Log:
Added credential cache to SimpleAuthenticator to improve Bind request throughput
by 27%. Also not another commit to follow for changes to LDAP shared.
Modified:
directory/branches/apacheds/optimization/core-unit/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticationITest.java
directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java
directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AuthenticationService.java
directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/Authenticator.java
directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java
Modified: directory/branches/apacheds/optimization/core-unit/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticationITest.java
URL: http://svn.apache.org/viewvc/directory/branches/apacheds/optimization/core-unit/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticationITest.java?rev=422808&r1=422807&r2=422808&view=diff
==============================================================================
--- directory/branches/apacheds/optimization/core-unit/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticationITest.java (original)
+++ directory/branches/apacheds/optimization/core-unit/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticationITest.java Mon Jul 17 12:21:14 2006
@@ -29,11 +29,13 @@
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.ModificationItem;
import javax.naming.ldap.InitialLdapContext;
import org.apache.directory.server.core.unit.AbstractAdminTestCase;
import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
import org.apache.directory.shared.ldap.util.ArrayUtils;
@@ -309,4 +311,156 @@
env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
assertNotNull( new InitialContext( env ) );
}
+
+
+ public void test11InvalidateCredentialCache() throws NamingException
+ {
+ Hashtable env = new Hashtable( configuration.toJndiEnvironment() );
+ env.put( Context.PROVIDER_URL, "ou=system" );
+ env.put( Context.SECURITY_PRINCIPAL, "uid=akarasulu,ou=users,ou=system" );
+ env.put( Context.SECURITY_CREDENTIALS, "test" );
+ env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+ InitialDirContext ic = new InitialDirContext( env );
+ Attributes attrs = ic.getAttributes( "uid=akarasulu,ou=users" );
+ Attribute ou = attrs.get( "ou" );
+ assertTrue( ou.contains( "Engineering" ) );
+ assertTrue( ou.contains( "People" ) );
+
+ Attribute objectClass = attrs.get( "objectClass" );
+ assertTrue( objectClass.contains( "top" ) );
+ assertTrue( objectClass.contains( "person" ) );
+ assertTrue( objectClass.contains( "organizationalPerson" ) );
+ assertTrue( objectClass.contains( "inetOrgPerson" ) );
+
+ assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
+ assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+ assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
+ assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
+ assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
+ assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
+ assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
+ assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
+ assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
+
+ // now modify the password for akarasulu
+ LockableAttributeImpl userPasswordAttribute = new LockableAttributeImpl( "userPassword", "newpwd" );
+ ic.modifyAttributes( "uid=akarasulu,ou=users", new ModificationItem[] {
+ new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+
+ // close and try with old password (should fail)
+ ic.close();
+ env.put( Context.SECURITY_CREDENTIALS, "test" );
+ try
+ {
+ ic = new InitialDirContext( env );
+ fail( "Authentication with old password should fail" );
+ }
+ catch ( NamingException e )
+ {
+ // we should fail
+ }
+
+ // close and try again now with new password (should fail)
+ ic.close();
+ env.put( Context.SECURITY_CREDENTIALS, "newpwd" );
+ ic = new InitialDirContext( env );
+ attrs = ic.getAttributes( "uid=akarasulu,ou=users" );
+ ou = attrs.get( "ou" );
+ assertTrue( ou.contains( "Engineering" ) );
+ assertTrue( ou.contains( "People" ) );
+
+ objectClass = attrs.get( "objectClass" );
+ assertTrue( objectClass.contains( "top" ) );
+ assertTrue( objectClass.contains( "person" ) );
+ assertTrue( objectClass.contains( "organizationalPerson" ) );
+ assertTrue( objectClass.contains( "inetOrgPerson" ) );
+
+ assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
+ assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+ assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
+ assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
+ assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
+ assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
+ assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
+ assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
+ assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
+ }
+
+
+ public void test12InvalidateCredentialCacheWithOID() throws NamingException
+ {
+ Hashtable env = new Hashtable( configuration.toJndiEnvironment() );
+ env.put( Context.PROVIDER_URL, "ou=system" );
+ env.put( Context.SECURITY_PRINCIPAL, "uid=akarasulu,ou=users,ou=system" );
+ env.put( Context.SECURITY_CREDENTIALS, "test" );
+ env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+ InitialDirContext ic = new InitialDirContext( env );
+ Attributes attrs = ic.getAttributes( "uid=akarasulu,ou=users" );
+ Attribute ou = attrs.get( "ou" );
+ assertTrue( ou.contains( "Engineering" ) );
+ assertTrue( ou.contains( "People" ) );
+
+ Attribute objectClass = attrs.get( "objectClass" );
+ assertTrue( objectClass.contains( "top" ) );
+ assertTrue( objectClass.contains( "person" ) );
+ assertTrue( objectClass.contains( "organizationalPerson" ) );
+ assertTrue( objectClass.contains( "inetOrgPerson" ) );
+
+ assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
+ assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+ assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
+ assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
+ assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
+ assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
+ assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
+ assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
+ assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
+
+ // now modify the password for akarasulu
+ LockableAttributeImpl userPasswordAttribute = new LockableAttributeImpl( "2.5.4.35", "newpwd" );
+ ic.modifyAttributes( "uid=akarasulu,ou=users", new ModificationItem[] {
+ new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+
+ // close and try with old password (should fail)
+ ic.close();
+ env.put( Context.SECURITY_CREDENTIALS, "test" );
+ try
+ {
+ ic = new InitialDirContext( env );
+ fail( "Authentication with old password should fail" );
+ }
+ catch ( NamingException e )
+ {
+ // we should fail
+ }
+
+ // close and try again now with new password (should fail)
+ ic.close();
+ env.put( Context.SECURITY_CREDENTIALS, "newpwd" );
+ ic = new InitialDirContext( env );
+ attrs = ic.getAttributes( "uid=akarasulu,ou=users" );
+ ou = attrs.get( "ou" );
+ assertTrue( ou.contains( "Engineering" ) );
+ assertTrue( ou.contains( "People" ) );
+
+ objectClass = attrs.get( "objectClass" );
+ assertTrue( objectClass.contains( "top" ) );
+ assertTrue( objectClass.contains( "person" ) );
+ assertTrue( objectClass.contains( "organizationalPerson" ) );
+ assertTrue( objectClass.contains( "inetOrgPerson" ) );
+
+ assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
+ assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+ assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
+ assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
+ assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
+ assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
+ assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
+ assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
+ assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
+ }
+
+
}
Modified: directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java
URL: http://svn.apache.org/viewvc/directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java?rev=422808&r1=422807&r2=422808&view=diff
==============================================================================
--- directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java (original)
+++ directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java Mon Jul 17 12:21:14 2006
@@ -129,6 +129,14 @@
public abstract LdapPrincipal authenticate( LdapDN bindDn, ServerContext ctx ) throws NamingException;
+
+ /**
+ * Does nothing leaving it so subclasses can override.
+ */
+ public void passwordChanged( LdapDN bindDn, byte[] userPassword )
+ {
+ }
+
/**
* Returns a new {@link LdapPrincipal} instance whose value is the specified
Modified: directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AuthenticationService.java
URL: http://svn.apache.org/viewvc/directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AuthenticationService.java?rev=422808&r1=422807&r2=422808&view=diff
==============================================================================
--- directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AuthenticationService.java (original)
+++ directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/AuthenticationService.java Mon Jul 17 12:21:14 2006
@@ -27,6 +27,7 @@
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.ModificationItem;
import javax.naming.directory.SearchControls;
@@ -42,7 +43,9 @@
import org.apache.directory.server.core.jndi.ServerContext;
import org.apache.directory.shared.ldap.exception.LdapAuthenticationException;
import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
import org.apache.directory.shared.ldap.util.AttributeUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.slf4j.Logger;
@@ -65,7 +68,7 @@
public Map authenticators = new HashMap();
private DirectoryServiceConfiguration factoryCfg;
-
+ private AttributeType userPasswordAttributeType;
/**
* Creates an authentication service interceptor.
@@ -81,6 +84,8 @@
public void init( DirectoryServiceConfiguration factoryCfg, InterceptorConfiguration cfg ) throws NamingException
{
this.factoryCfg = factoryCfg;
+ this.userPasswordAttributeType = factoryCfg.getGlobalRegistries()
+ .getAttributeTypeRegistry().lookup( "userPassword" );
// Register all authenticators
Iterator i = factoryCfg.getStartupConfiguration().getAuthenticatorConfigurations().iterator();
@@ -324,8 +329,39 @@
checkAuthenticated();
next.modify( name, modOp, mods );
+
+ Attribute userPasswordAttribute = AttributeUtils.getAttribute( mods, userPasswordAttributeType );
+ if ( userPasswordAttribute != null )
+ {
+ notifyUserPasswordChanged( userPasswordAttribute );
+ }
}
+
+ private byte[] notifyUserPasswordChanged( Attribute userPasswordAttribute ) throws NamingException
+ {
+ byte[] passwordBytes = null;
+ Object password = userPasswordAttribute.get();
+ if ( password instanceof byte[] )
+ {
+ passwordBytes = ( byte[] ) password;
+ }
+ else
+ {
+ passwordBytes = StringTools.getBytesUtf8( ( String ) password );
+ }
+
+ Collection authenticators = getAuthenticators( "simple" );
+ // try each authenticators
+ for ( Iterator i = authenticators.iterator(); i.hasNext(); )
+ {
+ Authenticator authenticator = ( Authenticator ) i.next();
+ authenticator.passwordChanged( getPrincipal().getJndiName(), passwordBytes );
+ }
+
+ return passwordBytes;
+ }
+
public void modify( NextInterceptor next, LdapDN name, ModificationItem[] mods ) throws NamingException
{
@@ -336,6 +372,12 @@
checkAuthenticated();
next.modify( name, mods );
+
+ Attribute userPasswordAttribute = AttributeUtils.getAttribute( mods, userPasswordAttributeType );
+ if ( userPasswordAttribute != null )
+ {
+ notifyUserPasswordChanged( userPasswordAttribute );
+ }
}
Modified: directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/Authenticator.java
URL: http://svn.apache.org/viewvc/directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/Authenticator.java?rev=422808&r1=422807&r2=422808&view=diff
==============================================================================
--- directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/Authenticator.java (original)
+++ directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/Authenticator.java Mon Jul 17 12:21:14 2006
@@ -65,6 +65,15 @@
*/
public void destroy();
+ /**
+ * Callback used to respond to password changes by invalidating a password
+ * cache if implemented. This is an additional feature of an authenticator
+ * which need not be implemented: empty implementation is sufficient.
+ *
+ * @param bindDn the already normalized distinguished name of the bind principal
+ * @param userPassword the new password for the bind principal
+ */
+ public void passwordChanged( LdapDN bindDn, byte[] userPassword );
/**
* Performs authentication and returns the principal if succeeded.
Modified: directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java
URL: http://svn.apache.org/viewvc/directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java?rev=422808&r1=422807&r2=422808&view=diff
==============================================================================
--- directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java (original)
+++ directory/branches/apacheds/optimization/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java Mon Jul 17 12:21:14 2006
@@ -23,6 +23,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import java.util.WeakHashMap;
import javax.naming.Context;
import javax.naming.NamingException;
@@ -38,6 +39,7 @@
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.util.ArrayUtils;
import org.apache.directory.shared.ldap.util.Base64;
+import org.apache.directory.shared.ldap.util.StringTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,9 +56,10 @@
public class SimpleAuthenticator extends AbstractAuthenticator
{
private static final Logger log = LoggerFactory.getLogger( SimpleAuthenticator.class );
-
private static final Collection USERLOOKUP_BYPASS;
+ private WeakHashMap credentialCache = new WeakHashMap( 1000 );
+
static
{
Set c = new HashSet();
@@ -81,7 +84,7 @@
super( "simple" );
}
-
+
/**
* Looks up <tt>userPassword</tt> attribute of the entry whose name is the
* value of {@link Context#SECURITY_PRINCIPAL} environment variable, and
@@ -102,6 +105,60 @@
creds = ( ( String ) creds ).getBytes();
}
+ byte[] userPassword = null;
+ if ( credentialCache.containsKey( principalDn.getNormName() ) )
+ {
+ userPassword = ( byte[] ) credentialCache.get( principalDn.getNormName() );
+ }
+ else
+ {
+ userPassword = lookupUserPassword( principalDn );
+ }
+
+ boolean credentialsMatch = false;
+
+ // Check if password is stored as a message digest, i.e. one-way
+ // encrypted
+ if ( this.isPasswordOneWayEncrypted( userPassword ) )
+ {
+ try
+ {
+ // create a corresponding digested password from creds
+ String algorithm = this.getAlgorithmForHashedPassword( userPassword );
+ String digestedCredits = this.createDigestedPassword( algorithm, creds );
+
+ credentialsMatch = ArrayUtils.isEquals( digestedCredits.getBytes(), userPassword );
+ }
+ catch ( NoSuchAlgorithmException nsae )
+ {
+ log.warn( "Password stored with unknown algorithm.", nsae );
+ }
+ catch ( IllegalArgumentException e )
+ {
+ log.warn( "Exception during authentication", e );
+ }
+ }
+ else
+ {
+ // password is not stored one-way encrypted
+ credentialsMatch = ArrayUtils.isEquals( creds, userPassword );
+ }
+
+ if ( credentialsMatch )
+ {
+ LdapPrincipal principal = new LdapPrincipal( principalDn, AuthenticationLevel.SIMPLE );
+ credentialCache.put( principalDn.getNormName(), userPassword );
+ return principal;
+ }
+ else
+ {
+ throw new LdapAuthenticationException();
+ }
+ }
+
+
+ protected byte[] lookupUserPassword( LdapDN principalDn ) throws NamingException
+ {
// ---- lookup the principal entry's userPassword attribute
Invocation invocation = InvocationStack.getInstance().peek();
@@ -132,8 +189,6 @@
// ---- assert that credentials match
- boolean credentialsMatch = false;
-
if ( userPasswordAttr == null )
{
userPassword = ArrayUtils.EMPTY_BYTE_ARRAY;
@@ -144,45 +199,11 @@
if ( userPassword instanceof String )
{
- userPassword = ( ( String ) userPassword ).getBytes();
+ userPassword = StringTools.getBytesUtf8( ( String ) userPassword );
}
}
-
- // Check if password is stored as a message digest, i.e. one-way
- // encrypted
- if ( this.isPasswordOneWayEncrypted( userPassword ) )
- {
- try
- {
- // create a corresponding digested password from creds
- String algorithm = this.getAlgorithmForHashedPassword( userPassword );
- String digestedCredits = this.createDigestedPassword( algorithm, creds );
-
- credentialsMatch = ArrayUtils.isEquals( digestedCredits.getBytes(), userPassword );
- }
- catch ( NoSuchAlgorithmException nsae )
- {
- log.warn( "Password stored with unknown algorithm.", nsae );
- }
- catch ( IllegalArgumentException e )
- {
- log.warn( "Exception during authentication", e );
- }
- }
- else
- {
- // password is not stored one-way encrypted
- credentialsMatch = ArrayUtils.isEquals( creds, userPassword );
- }
-
- if ( credentialsMatch )
- {
- return new LdapPrincipal( principalDn, AuthenticationLevel.SIMPLE );
- }
- else
- {
- throw new LdapAuthenticationException();
- }
+
+ return ( byte[] ) userPassword;
}
@@ -321,5 +342,11 @@
result.append( encoded );
return result.toString();
+ }
+
+
+ public void passwordChanged( LdapDN bindDn, byte[] userPassword )
+ {
+ credentialCache.put( bindDn.getNormName(), userPassword );
}
}