Author: kayyagari
Date: Mon Nov 1 09:44:10 2010
New Revision: 1029585
URL: http://svn.apache.org/viewvc?rev=1029585&view=rev
Log:
o adding a new interceptor for hashing passwords (while performing add and modify operations)
o associated test cases
Added:
directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/add/PasswordHashingInterceptorTest.java
directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/
directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/Md5PasswordHashingInterceptor.java
directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/PasswordHashingInterceptor.java
Added: directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/add/PasswordHashingInterceptorTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/add/PasswordHashingInterceptorTest.java?rev=1029585&view=auto
==============================================================================
--- directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/add/PasswordHashingInterceptorTest.java (added)
+++ directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/add/PasswordHashingInterceptorTest.java Mon Nov 1 09:44:10 2010
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.server.core.operations.add;
+
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.server.core.annotations.ApplyLdifs;
+import org.apache.directory.server.core.annotations.CreateDS;
+import org.apache.directory.server.core.authn.PasswordUtil;
+import org.apache.directory.server.core.hash.Md5PasswordHashingInterceptor;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.apache.directory.server.core.integ.IntegrationUtils;
+import org.apache.directory.shared.ldap.constants.LdapSecurityConstants;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.DefaultEntry;
+import org.apache.directory.shared.ldap.entry.DefaultEntryAttribute;
+import org.apache.directory.shared.ldap.entry.DefaultModification;
+import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.name.DN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test case for checking PasswordHashingInterceptor.
+ *
+ * @author Apache Directory Project
+ */
+@RunWith(FrameworkRunner.class)
+@CreateDS(name = "PasswordHashingInterceptorTest-DS", additionalInterceptors=Md5PasswordHashingInterceptor.class)
+@ApplyLdifs( {
+ "dn: cn=test,ou=system",
+ "objectClass: person",
+ "cn: test",
+ "sn: sn_test",
+ "userPassword: secret"
+})
+public class PasswordHashingInterceptorTest extends AbstractLdapTestUnit
+{
+
+ @Test
+ public void testAddWithPlainPassword() throws Exception
+ {
+ LdapConnection connection = IntegrationUtils.getAdminConnection( service );
+
+ byte[] plainPwd = "secret".getBytes();
+ DN dn = new DN( "cn=test,ou=system" );
+
+ Entry entry = connection.lookup( dn );
+ EntryAttribute pwdAt = entry.get( SchemaConstants.USER_PASSWORD_AT );
+
+ assertFalse( Arrays.equals( plainPwd, pwdAt.getBytes() ) );
+ assertTrue( PasswordUtil.compareCredentials( plainPwd, pwdAt.getBytes() ) );
+ }
+
+
+ @Test
+ public void testModifyWithPlainPassword() throws Exception
+ {
+ LdapConnection connection = IntegrationUtils.getAdminConnection( service );
+
+ byte[] plainPwd = "newsecret".getBytes();
+ DN dn = new DN( "cn=test,ou=system" );
+
+ AttributeType pwdAtType = service.getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.USER_PASSWORD_AT );
+
+ EntryAttribute pwdAt = new DefaultEntryAttribute( pwdAtType );
+ pwdAt.add( plainPwd );
+
+ Modification mod = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, pwdAt );
+ connection.modify( dn, mod );
+
+ Entry entry = connection.lookup( dn );
+ pwdAt = entry.get( pwdAtType );
+
+ assertFalse( Arrays.equals( plainPwd, pwdAt.getBytes() ) );
+ assertTrue( PasswordUtil.compareCredentials( plainPwd, pwdAt.getBytes() ) );
+ }
+
+
+ @Test
+ public void testAddWithHashedPassword() throws Exception
+ {
+ LdapConnection connection = IntegrationUtils.getAdminConnection( service );
+
+ byte[] plainPwd = "secret".getBytes();
+ byte[] hashedPwd = PasswordUtil.createStoragePassword( plainPwd, LdapSecurityConstants.HASH_METHOD_SSHA );
+
+ DN dn = new DN( "cn=testHash,ou=system" );
+ Entry entry = new DefaultEntry( service.getSchemaManager(), dn );
+ entry.add( "ObjectClass", "top", "person" );
+ entry.add( "sn", "TEST" );
+ entry.add( "cn", "testHash" );
+ entry.add( SchemaConstants.USER_PASSWORD_AT, hashedPwd );
+
+ connection.add( entry );
+
+ entry = connection.lookup( dn );
+ EntryAttribute pwdAt = entry.get( SchemaConstants.USER_PASSWORD_AT );
+ assertTrue( Arrays.equals( hashedPwd, pwdAt.getBytes() ) );
+ assertTrue( PasswordUtil.compareCredentials( plainPwd, pwdAt.getBytes() ) );
+ }
+
+
+ @Test
+ public void testModifyWithHashedPassword() throws Exception
+ {
+ LdapConnection connection = IntegrationUtils.getAdminConnection( service );
+
+ byte[] plainPwd = "xyzsecret".getBytes();
+ byte[] hashedPwd = PasswordUtil.createStoragePassword( plainPwd, LdapSecurityConstants.HASH_METHOD_SSHA256 );
+
+ DN dn = new DN( "cn=test,ou=system" );
+
+ AttributeType pwdAtType = service.getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.USER_PASSWORD_AT );
+
+ EntryAttribute pwdAt = new DefaultEntryAttribute( pwdAtType );
+ pwdAt.add( hashedPwd );
+
+ Modification mod = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, pwdAt );
+ connection.modify( dn, mod );
+
+ Entry entry = connection.lookup( dn );
+ pwdAt = entry.get( pwdAtType );
+
+ assertTrue( Arrays.equals( hashedPwd, pwdAt.getBytes() ) );
+ assertTrue( PasswordUtil.compareCredentials( plainPwd, pwdAt.getBytes() ) );
+ }
+}
Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/Md5PasswordHashingInterceptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/Md5PasswordHashingInterceptor.java?rev=1029585&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/Md5PasswordHashingInterceptor.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/Md5PasswordHashingInterceptor.java Mon Nov 1 09:44:10 2010
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.server.core.hash;
+
+import org.apache.directory.shared.ldap.constants.LdapSecurityConstants;
+
+/**
+ * PasswordHashingInterceptor using MD5 hashing algorithm.
+ *
+ * @author Apache Directory Project
+ */
+public class Md5PasswordHashingInterceptor extends PasswordHashingInterceptor
+{
+ public Md5PasswordHashingInterceptor()
+ {
+ super( LdapSecurityConstants.HASH_METHOD_MD5 );
+ }
+}
Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/PasswordHashingInterceptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/PasswordHashingInterceptor.java?rev=1029585&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/PasswordHashingInterceptor.java (added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/hash/PasswordHashingInterceptor.java Mon Nov 1 09:44:10 2010
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.server.core.hash;
+
+
+import java.util.List;
+
+import org.apache.directory.server.core.authn.PasswordUtil;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.shared.ldap.constants.LdapSecurityConstants;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.BinaryValue;
+import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.exception.LdapException;
+
+
+/**
+ * An interceptor to hash plain text password according to the configured
+ * hashing algorithm.
+ *
+ * @author Apache Directory Project
+ */
+public class PasswordHashingInterceptor extends BaseInterceptor
+{
+
+ /** the hashing algorithm to be used, if null then the password won't be changed */
+ private LdapSecurityConstants algorithm;
+
+
+ /**
+ * Creates a new instance of PasswordHashingInterceptor which does not hash the passowrds.
+ */
+ public PasswordHashingInterceptor()
+ {
+ this( null );
+ }
+
+
+ /**
+ *
+ * Creates a new instance of PasswordHashingInterceptor which hashes the
+ * incoming non-hashed password using the given algorithm.
+ * If the password is found already hashed then it will skip hashing it.
+ *
+ * @param algorithm the name of the algorithm to be used
+ */
+ public PasswordHashingInterceptor( LdapSecurityConstants algorithm )
+ {
+ this.algorithm = algorithm;
+ }
+
+
+ @Override
+ public void add( NextInterceptor next, AddOperationContext addContext ) throws LdapException
+ {
+ if ( algorithm == null )
+ {
+ next.add( addContext );
+ return;
+ }
+
+ Entry entry = addContext.getEntry();
+
+ EntryAttribute pwdAt = entry.get( SchemaConstants.USER_PASSWORD_AT );
+
+ includeHashedPassword( pwdAt );
+
+ next.add( addContext );
+ }
+
+
+ @Override
+ public void modify( NextInterceptor next, ModifyOperationContext modifyContext ) throws LdapException
+ {
+ if ( algorithm == null )
+ {
+ next.modify( modifyContext );
+ return;
+ }
+
+ List mods = modifyContext.getModItems();
+
+ for ( Modification mod : mods )
+ {
+ String oid = mod.getAttribute().getAttributeType().getOid();
+
+ // check for modification on 'userPassword' AT
+ if ( SchemaConstants.USER_PASSWORD_AT_OID.equals( oid ) )
+ {
+ includeHashedPassword( mod.getAttribute() );
+ break;
+ }
+ }
+
+ next.modify( modifyContext );
+ }
+
+
+ /**
+ * hash the password if it was not already hashed
+ *
+ * @param pwdAt the password attribute
+ */
+ private void includeHashedPassword( EntryAttribute pwdAt )
+ {
+ if ( pwdAt == null )
+ {
+ return;
+ }
+
+ BinaryValue userPassword = ( BinaryValue ) pwdAt.get();
+
+ // check if the given password is already hashed
+ LdapSecurityConstants existingAlgo = PasswordUtil.findAlgorithm( userPassword.get() );
+
+ // if there exists NO algorithm, then hash the password
+ if ( existingAlgo == null )
+ {
+ byte[] hashedPassword = PasswordUtil.createStoragePassword( userPassword.get(), algorithm );
+
+ pwdAt.clear();
+ pwdAt.add( hashedPassword );
+ }
+ }
+}