Added a test for password modify
diff --git a/server-integ/src/test/java/org/apache/directory/server/operations/extended/PwdModifyNoPPNoPasswordHashingIT.java b/server-integ/src/test/java/org/apache/directory/server/operations/extended/PwdModifyNoPPNoPasswordHashingIT.java
new file mode 100644
index 0000000..1dd8a77
--- /dev/null
+++ b/server-integ/src/test/java/org/apache/directory/server/operations/extended/PwdModifyNoPPNoPasswordHashingIT.java
@@ -0,0 +1,1474 @@
+/*
+ *  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.operations.extended;
+
+
+import static org.apache.directory.server.core.integ.IntegrationUtils.getAdminNetworkConnection;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getAnonymousNetworkConnection;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getNetworkConnectionAs;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyRequest;
+import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyRequestImpl;
+import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyResponse;
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.message.AddRequest;
+import org.apache.directory.api.ldap.model.message.AddRequestImpl;
+import org.apache.directory.api.ldap.model.message.AddResponse;
+import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.password.PasswordUtil;
+import org.apache.directory.api.util.Strings;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.CreateDS;
+import org.apache.directory.server.core.api.InterceptorEnum;
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authn.ppolicy.PpolicyConfigContainer;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.apache.directory.server.ldap.handlers.extended.PwdModifyHandler;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Test the PwdModify extended operation
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+@RunWith(FrameworkRunner.class)
+@CreateLdapServer(
+    transports =
+        { @CreateTransport(protocol = "LDAP") },
+    extendedOpHandlers =
+        { PwdModifyHandler.class },
+    allowAnonymousAccess = true)
+@CreateDS(name = "PasswordPolicyTest")
+public class PwdModifyNoPPNoPasswordHashingIT extends AbstractLdapTestUnit
+{
+    /**
+     * Add a user with a password
+     */
+    private void addUser( LdapConnection adminConnection, String user, Object password ) throws Exception
+    {
+        Entry userEntry = new DefaultEntry(
+            "cn=" + user + ",ou=system",
+            "ObjectClass: top",
+            "ObjectClass: person",
+            "cn", user,
+            "sn", user + "_sn",
+            "userPassword", password );
+
+        AddRequest addRequest = new AddRequestImpl();
+        addRequest.setEntry( userEntry );
+
+        AddResponse addResp = adminConnection.add( addRequest );
+        assertEquals( ResultCodeEnum.SUCCESS, addResp.getLdapResult().getResultCode() );
+    }
+    
+    
+    /**
+     * Add a user with no password
+     */
+    private void addUserNoPassword( LdapConnection adminConnection, String user ) throws Exception
+    {
+        Entry userEntry = new DefaultEntry(
+            "cn=" + user + ",ou=system",
+            "ObjectClass: top",
+            "ObjectClass: person",
+            "cn", user,
+            "sn", user + "_sn" );
+
+        AddRequest addRequest = new AddRequestImpl();
+        addRequest.setEntry( userEntry );
+
+        AddResponse addResp = adminConnection.add( addRequest );
+        assertEquals( ResultCodeEnum.SUCCESS, addResp.getLdapResult().getResultCode() );
+    }
+
+
+    /**
+     * Add a user with 2 passwords
+     */
+    private void addUser2Passwords( LdapConnection adminConnection, String user, Object password1, Object password2 ) throws Exception
+    {
+        Entry userEntry = new DefaultEntry(
+            "cn=" + user + ",ou=system",
+            "ObjectClass: top",
+            "ObjectClass: person",
+            "cn", user,
+            "sn", user + "_sn",
+            "userPassword", password1, 
+            "userPassword", password2 );
+
+        AddRequest addRequest = new AddRequestImpl();
+        addRequest.setEntry( userEntry );
+
+        AddResponse addResp = adminConnection.add( addRequest );
+        assertEquals( ResultCodeEnum.SUCCESS, addResp.getLdapResult().getResultCode() );
+    }
+
+
+    //-----------------------------------------------------------------------------------
+    // Self password modification  with one password
+    //-----------------------------------------------------------------------------------
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is not provided
+     * o the new password is new
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwnPasswordNoOldNew() throws Exception
+    {
+        // Create a user
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {        
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Bind as the user
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" ) )
+            {
+                Entry user = userConnection.lookup( "cn=user1,ou=system", "modifyTimestamp" );
+                
+                // Now change the password
+                PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+                pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1Bis" ) );
+        
+                // Send the request
+                PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+        
+                assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+        
+                // Now try to bind with the new password
+                try ( LdapConnection newUserConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" ) )
+                {
+                    Entry entry = newUserConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+            
+                    assertNotNull( entry );
+                    assertTrue( entry.containsAttribute( "userPassword" ) );
+                    Attribute userPassword = entry.get( "userPassword" );
+                    
+                    assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+            
+                    assertEquals( 1, userPassword.size() );
+                    assertEquals( "secret1Bis", userPassword.getString() );
+                    assertNull( user.get( "modifyTimestamp" ) );
+                    assertNotNull( entry.get( "modifyTimestamp" ).getString() );
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is not provided
+     * o the new password exists
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwnPasswordNoOldNewExists() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User1", "secret1" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        Entry user = userConnection.lookup( "cn=user1,ou=system", "modifyTimestamp" );
+        
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Now try to bind with the new password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1", userPassword.getString() );
+        assertNull( user.get( "modifyTimestamp" ) );
+        assertNull( entry.get( "modifyTimestamp" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is provided
+     * o the new password is new
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwnPasswordOldNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User1", "secret1" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1Bis" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Now try to bind with the new password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1Bis", userPassword.getString() );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is provided, but it's the wrong one
+     * o the new password is new
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwnPasswordInvalidOldNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User1", "secret1" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), 
+            "cn=user1,ou=system", "secret1" );
+        
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret2" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1Bis" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.INVALID_CREDENTIALS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Now try to bind with the new password. Should fail
+        try
+        {
+            userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" );
+            fail();
+        }
+        catch ( LdapAuthenticationException lae )
+        {
+            // We are fine
+        }
+
+        // Rebind with the original password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1", userPassword.getString() );
+        
+        // The entry should not have been modified
+        assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is provided
+     * o the new password is not provided 
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwnPasswordOldNoNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User1", "secret1" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Rebind with the original password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1", userPassword.getString() );
+        assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is provided
+     * o the new password is already existing
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwnPasswordOldNewExists() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User1", "secret1" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Rebind with the original password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1", userPassword.getString() );
+        assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is not provided
+     * o the new password is not provided
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwnPasswordNoOldNoNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User1", "secret1" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Rebind with the original password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1", userPassword.getString() );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    //-----------------------------------------------------------------------------------
+    // Self password modification with two passwords
+    //-----------------------------------------------------------------------------------
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is not provided
+     * o the new password is not provided
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains two values
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwn2PasswordsNoOldNoNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Rebind with the original password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 2, userPassword.size() );
+        assertTrue( userPassword.contains( "secret1", "other" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+    
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is not provided
+     * o the new password is new
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains two values
+     * 
+     * At the end, we will have only one password remaining
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwn2PassworsdNoOldNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        Entry user = userConnection.lookup( "cn=user1,ou=system", "modifyTimestamp" );
+        
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1Bis" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Now try to bind with the new password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1Bis", userPassword.getString() );
+        assertNull( user.get( "modifyTimestamp" ) );
+        assertNotNull( entry.get( "modifyTimestamp" ).getString() );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is not provided
+     * o the new password exists
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains two values
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwn2PasswordsNoOldNewExists() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        Entry user = userConnection.lookup( "cn=user1,ou=system", "modifyTimestamp" );
+        
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Now try to bind with the new password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 2, userPassword.size() );
+        assertTrue( userPassword.contains( "secret1", "other" ) );
+        assertNull( user.get( "modifyTimestamp" ) );
+        assertNull( entry.get( "modifyTimestamp" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is provided
+     * o the new password is new
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains two values
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwn2PasswordsOldNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1Bis" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Now try to bind with the new password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1Bis", userPassword.getString() );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is provided, but it's the wrong one
+     * o the new password is new
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains two values
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwn2PasswordsInvalidOldNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), 
+            "cn=user1,ou=system", "secret1" );
+        
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret2" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1Bis" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.INVALID_CREDENTIALS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Now try to bind with the new password. Should fail
+        try
+        {
+            userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" );
+            fail();
+        }
+        catch ( LdapAuthenticationException lae )
+        {
+            // We are fine
+        }
+
+        // Rebind with the original password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 2, userPassword.size() );
+        assertTrue( userPassword.contains( "secret1", "other" ) );
+        
+        // The entry should not have been modified
+        assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is provided
+     * o the new password is not provided 
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains two values
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwn2PasswordsOldNoNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Rebind with the original password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 2, userPassword.size() );
+        assertTrue( userPassword.contains( "secret1", "other" ) );
+        assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password while the user is connected:
+     * o the userIdentity is not provided
+     * o the old password is provided
+     * o the new password is already existing
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains two values
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testModifyOwn2PasswordsOldNewExists() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+        // Bind as the user
+        LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Rebind with the original password
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1" );
+
+        Entry entry = userConnection.lookup( "cn=User1,ou=system" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 2, userPassword.size() );
+        assertTrue( userPassword.contains( "secret1", "other" ) );
+        assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+    
+    
+    //-----------------------------------------------------------------------------------
+    // Non connected user
+    //-----------------------------------------------------------------------------------
+    /**
+     * Modify an existing user password while the user is not connected
+     */
+    @Test
+    public void testModifyUserPasswordAnonymous() throws Exception
+    {
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User2", "secret2" );
+
+        LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User2,ou=system", "secret2" );
+
+        Entry entry = userConnection.lookup( "cn=User2,ou=system" );
+
+        assertNotNull( entry );
+
+        userConnection.close();
+
+        // Anonymous Bind
+        LdapConnection anonymousConnection = getAnonymousNetworkConnection( getLdapServer() );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User2,ou=system" ) );
+        pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret2" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret2Bis" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) anonymousConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Check that we can now bind using the new credentials
+        userConnection = getNetworkConnectionAs( ldapServer, "cn=User2,ou=system", "secret2Bis" );
+
+        entry = userConnection.lookup( "cn=User2,ou=system" );
+
+        assertNotNull( entry );
+
+        userConnection.close();
+        anonymousConnection.close();
+        adminConnection.close();
+    }
+    
+    
+    /**
+     * Test that the server refuse to generate a password
+     */
+    @Test
+    public void testOwnGenPassword() throws Exception
+    {
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User6", "secret6" );
+
+        // Modify the user with the user account
+        LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User6,ou=system", "secret6" );
+
+        // Now request a new password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User6,ou=system" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) userConnection.extended( pwdModifyRequest );
+
+        // We should not be allowed to do that, as the operation is not yet implemented
+        assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        userConnection.close();
+        adminConnection.close();
+    }
+
+
+    //-----------------------------------------------------------------------------------
+    // With admin
+    //-----------------------------------------------------------------------------------
+    /**
+     * Modify an existing user password with an admin account
+     */
+    @Test
+    public void testAdminModifyPassword() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            addUserNoPassword( adminConnection, "User4" );
+    
+            // Modify the user with the admin account
+            Entry entry = adminConnection.lookup( "cn=User4,ou=system" );
+    
+            assertNotNull( entry );
+    
+            // Now change the password
+            PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+            pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User4,ou=system" ) );
+            pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret4" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            // Now try to bind with the new password
+            try( LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User4,ou=system", "secret4" ))
+            {
+                fail();
+            }
+            catch ( LdapException le )
+            {
+                // expected
+            }
+    
+            entry = adminConnection.lookup( "cn=User4,ou=system", SchemaConstants.ALL_ATTRIBUTES_ARRAY );
+    
+            assertNotNull( entry );
+            assertFalse( entry.containsAttribute( "userPassword" ) );
+            assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with admin
+     * o the userIdentity is provided
+     * o the old password is not provided
+     * o the new password is not provided
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testAdminModifyPasswordNoOldNoNew() throws Exception
+    {
+        // Create a user
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User1", "secret1" );
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User1,ou=system" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        Entry entry = adminConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+        assertNotNull( entry );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        Attribute userPassword = entry.get( "userPassword" );
+        
+        assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+
+        assertEquals( 1, userPassword.size() );
+        assertEquals( "secret1", userPassword.getString() );
+        assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+
+        adminConnection.close();
+    }
+
+
+    /**
+     * Modify an existing user password with admin
+     * o the userIdentity is provided
+     * o the old password is not provided
+     * o the new password is provided
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testAdminModifyPasswordNoOldNew() throws Exception
+    {
+        // Create a user
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Now change the password
+            PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+            pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User1,ou=system" ) );
+            pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1Bis" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), 
+                "cn=user1,ou=system", "secret1Bis" ) )
+            {
+                Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+            
+                assertNotNull( entry );
+                assertTrue( entry.containsAttribute( "userPassword" ) );
+                Attribute userPassword = entry.get( "userPassword" );
+                
+                assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+            
+                assertEquals( 1, userPassword.size() );
+                assertEquals( "secret1Bis", userPassword.getString() );
+                assertTrue( entry.containsAttribute( "modifyTimestamp" ) );
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user with no password with admin
+     * o the userIdentity is provided
+     * o the old password is not provided
+     * o the new password is provided
+     * o the entry does not have a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testAdminAddPasswordNoOldNew() throws Exception
+    {
+        // Create a user
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            addUserNoPassword( adminConnection, "User1" );
+    
+            // Now change the password
+            PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+            pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User1,ou=system" ) );
+            pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), 
+                "cn=user1,ou=system", "secret1" ) )
+            {
+                fail();
+            }
+            catch ( LdapException le )
+            {
+                // expected
+            }
+
+            Entry entry = adminConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+        
+            assertNotNull( entry );
+            assertFalse( entry.containsAttribute( "userPassword" ) );
+            assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+        }
+    }
+    
+
+    /**
+     * Modify an existing user password with admin
+     * o the userIdentity is provided
+     * o the old password is provided but is invalid
+     * o the new password is provided
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testAdminModifyPasswordInvalidOldNew() throws Exception
+    {
+        // Create a user
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            addUser( adminConnection, "User1", "secret1" );
+        
+            // Now change the password
+            PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+            pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User1,ou=system" ) );
+            pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret2" ) );
+            pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret2" ) ); 
+        
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+        
+            assertEquals( ResultCodeEnum.INVALID_CREDENTIALS, pwdModifyResponse.getLdapResult().getResultCode() );
+        
+            Entry entry = adminConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+        
+            assertNotNull( entry );
+            assertTrue( entry.containsAttribute( "userPassword" ) );
+            Attribute userPassword = entry.get( "userPassword" );
+            
+            assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+        
+            assertEquals( 1, userPassword.size() );
+            assertEquals( "secret1", userPassword.getString() );
+            assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with admin
+     * o the userIdentity is provided
+     * o the old password is not provided
+     * o the new password is provided but already exist
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testAdminModifyPasswordNoOldNewExists() throws Exception
+    {
+        // Create a user
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Now change the password
+            PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+            pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User1,ou=system" ) );
+            pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), 
+                "cn=user1,ou=system", "secret1" ) )
+            {
+                Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+            
+                assertNotNull( entry );
+                assertTrue( entry.containsAttribute( "userPassword" ) );
+                Attribute userPassword = entry.get( "userPassword" );
+                
+                assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+            
+                assertEquals( 1, userPassword.size() );
+                assertEquals( "secret1", userPassword.getString() );
+                assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with admin
+     * o the userIdentity is provided
+     * o the old password is provided
+     * o the new password is provided
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disable
+     */
+    @Test
+    public void testAdminModifyPasswordOldNew() throws Exception
+    {
+        // Create a user
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Now change the password
+            PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+            pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User1,ou=system" ) );
+            pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+            pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1Bis" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), 
+                "cn=user1,ou=system", "secret1Bis" ) )
+            {
+                Entry entry = userConnection.lookup( "cn=User1,ou=system", SchemaConstants.ALL_ATTRIBUTES_ARRAY );
+            
+                System.out.println( entry );
+                assertNotNull( entry );
+                assertTrue( entry.containsAttribute( "userPassword" ) );
+                Attribute userPassword = entry.get( "userPassword" );
+                
+                assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+            
+                assertEquals( 1, userPassword.size() );
+                assertEquals( "secret1Bis", userPassword.getString() );
+                assertTrue( entry.containsAttribute( "modifyTimestamp" ) );
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with admin
+     * o the userIdentity is provided
+     * o the old password is provided
+     * o the new password is not provided
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disable
+     */
+    @Test
+    public void testAdminModifyPasswordOldNoNew() throws Exception
+    {
+        // Create a user
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Now change the password
+            PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+            pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User1,ou=system" ) );
+            pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), 
+                "cn=user1,ou=system", "secret1" ) )
+            {
+                Entry entry = userConnection.lookup( "cn=User1,ou=system", SchemaConstants.ALL_ATTRIBUTES_ARRAY );
+            
+                assertNotNull( entry );
+                assertTrue( entry.containsAttribute( "userPassword" ) );
+                Attribute userPassword = entry.get( "userPassword" );
+                
+                assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+            
+                assertEquals( 1, userPassword.size() );
+                assertEquals( "secret1", userPassword.getString() );
+                assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with admin
+     * o the userIdentity is provided
+     * o the old password is provided
+     * o the new password is provided
+     * o the entry has a userPassword attribute
+     * o the userPassword attribute contains only one value
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testAdminModifyPasswordOldNewExists() throws Exception
+    {
+        // Create a user
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Now change the password
+            PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+            pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User1,ou=system" ) );
+            pwdModifyRequest.setOldPassword( Strings.getBytesUtf8( "secret1" ) );
+            pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret1" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), 
+                "cn=user1,ou=system", "secret1" ) )
+            {
+                Entry entry = userConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+            
+                assertNotNull( entry );
+                assertTrue( entry.containsAttribute( "userPassword" ) );
+                Attribute userPassword = entry.get( "userPassword" );
+                
+                assertNull( PasswordUtil.findAlgorithm( userPassword.getBytes() ) );
+            
+                assertEquals( 1, userPassword.size() );
+                assertEquals( "secret1", userPassword.getString() );
+                assertFalse( entry.containsAttribute( "modifyTimestamp" ) );
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with an admin account
+     */
+    @Test
+    public void testAdminModifyMultiplePassword() throws Exception
+    {
+        AuthenticationInterceptor authenticationInterceptor = ( AuthenticationInterceptor ) getService()
+            .getInterceptor( InterceptorEnum.AUTHENTICATION_INTERCEPTOR.getName() );
+
+        PpolicyConfigContainer policyContainer = authenticationInterceptor.getPwdPolicyContainer();
+        authenticationInterceptor.setPwdPolicies( null );
+        
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser2Passwords( adminConnection, "User5", "secret51", "secret52" );
+
+        // Modify the user with the admin account
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User5,ou=system" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret5Bis" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        // Now try to bind with the new password
+        LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User5,ou=system", "secret5Bis" );
+
+        Entry entry = userConnection.lookup( "cn=User5,ou=system" );
+
+        assertNotNull( entry );
+
+        userConnection.close();
+        adminConnection.close();
+        authenticationInterceptor.setPwdPolicies( policyContainer );
+    }
+
+
+    /**
+     * Modify an existing user password with a bad account
+     */
+    @Test
+    public void testAdminModifyPasswordBadUser() throws Exception
+    {
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User5", "secret5" );
+
+        // Modify the user with the admin account
+
+        // Now change the password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=baduser,ou=system" ) );
+        pwdModifyRequest.setNewPassword( Strings.getBytesUtf8( "secret5Bis" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+
+        assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        adminConnection.close();
+    }
+
+
+    /**
+     * Test that the server refuse to generate a password
+     */
+    @Test
+    public void testAdminGenPassword() throws Exception
+    {
+        LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() );
+
+        addUser( adminConnection, "User6", "secret6" );
+
+        // Modify the user with the admin account
+
+        // Now request a new password
+        PasswordModifyRequest pwdModifyRequest = new PasswordModifyRequestImpl();
+        pwdModifyRequest.setUserIdentity( Strings.getBytesUtf8( "cn=User6,ou=system" ) );
+
+        // Send the request
+        PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+
+        // We should not be allowed to do that, as the operation is not yet implemented
+        assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, pwdModifyResponse.getLdapResult().getResultCode() );
+
+        adminConnection.close();
+    }
+}