You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2019/07/29 14:14:53 UTC

[directory-server] branch master updated: o Rewritten the pwdModifyHandler to cover more corner cases o Added a full set of tests for the PasswordModify extended request when the PPolicy is disabled and the passwordHashing interceptor is not set o Added the getIoSession() method in the DefaultCoreSession class

This is an automated email from the ASF dual-hosted git repository.

elecharny pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-server.git


The following commit(s) were added to refs/heads/master by this push:
     new 294813d  o Rewritten the pwdModifyHandler to cover more corner cases o Added a full set of tests for the PasswordModify extended request when the PPolicy is disabled and the passwordHashing interceptor is not set o Added the getIoSession() method in the DefaultCoreSession class
294813d is described below

commit 294813d86dd88b291e37c663718061c45c7e8e05
Author: emmanuel lecharny <el...@apache.org>
AuthorDate: Mon Jul 29 16:14:26 2019 +0200

    o Rewritten the pwdModifyHandler to cover more corner cases
    o Added a full set of tests for the PasswordModify extended request when
    the PPolicy is disabled and the passwordHashing interceptor is not set
    o Added the getIoSession() method in the DefaultCoreSession class
---
 .../server/core/shared/DefaultCoreSession.java     |   11 +
 .../ldap/handlers/extended/PwdModifyHandler.java   |  448 +++--
 .../server/operations/extended/PwdModifyIT.java    |    2 +
 .../extended/PwdModifyNoPPNoPasswordHashingIT.java | 1828 ++++++++++++++++++++
 4 files changed, 2152 insertions(+), 137 deletions(-)

diff --git a/core-shared/src/main/java/org/apache/directory/server/core/shared/DefaultCoreSession.java b/core-shared/src/main/java/org/apache/directory/server/core/shared/DefaultCoreSession.java
index e1361d7..043b739 100644
--- a/core-shared/src/main/java/org/apache/directory/server/core/shared/DefaultCoreSession.java
+++ b/core-shared/src/main/java/org/apache/directory/server/core/shared/DefaultCoreSession.java
@@ -169,6 +169,17 @@ public class DefaultCoreSession implements CoreSession
 
 
     /**
+     * Gets the IoSession from the CoreSession. This is only useful when the server is not embedded.
+     * 
+     * @return ioSession The IoSession for this CoreSession
+     */
+    public IoSession getIoSession()
+    {
+        return ioSession;
+    }
+
+
+    /**
      * Stores the IoSession into the CoreSession. This is only useful when the server is not embedded.
      * 
      * @param ioSession The IoSession for this CoreSession
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/PwdModifyHandler.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/PwdModifyHandler.java
index 16f02eb..7634ea4 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/PwdModifyHandler.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/PwdModifyHandler.java
@@ -30,12 +30,14 @@ import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyRes
 import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyResponseImpl;
 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.DefaultAttribute;
 import org.apache.directory.api.ldap.model.entry.DefaultModification;
 import org.apache.directory.api.ldap.model.entry.Entry;
 import org.apache.directory.api.ldap.model.entry.Modification;
 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
 import org.apache.directory.api.ldap.model.entry.Value;
 import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
 import org.apache.directory.api.ldap.model.exception.LdapOperationException;
 import org.apache.directory.api.ldap.model.message.Control;
@@ -48,6 +50,7 @@ import org.apache.directory.api.util.Strings;
 import org.apache.directory.server.core.api.CoreSession;
 import org.apache.directory.server.core.api.DirectoryService;
 import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.shared.DefaultCoreSession;
 import org.apache.directory.server.ldap.ExtendedOperationHandler;
 import org.apache.directory.server.ldap.LdapServer;
 import org.apache.directory.server.ldap.LdapSession;
@@ -88,157 +91,355 @@ public class PwdModifyHandler implements ExtendedOperationHandler<PasswordModify
 
     /**
      * Modify the user's credentials.
+     * 
+     * We will need to modify the userPassword attribute, accordingly to a few rules:
+     * - if the old password is present, we should verify it's valid. if not, we return an error
+     * - if the old password is absent, we are modifying the password of the current used.
+     * - if the new password is absent, we will return an error. The RFC says that we could
+     * generate a random password, but that would be dangerous to do so.
+     * - if the new password already exists, we simply return not changing anything 
+     * - otherwise, we just remove the old password from the list of passwords (we may have 
+     * more than one) and add the new password. This is done with a REPLACE operation (Modify)
      */
-    private void modifyUserPassword( CoreSession userSession, IoSession ioPipe, Dn userDn, byte[] oldPassword, byte[] newPassword,
-        PasswordModifyRequest req )
+    private void modifyUserPassword( CoreSession userSession, Entry userEntry, Dn userDn, 
+        byte[] oldPassword, byte[] newPassword, PasswordModifyRequest req )
     {
-        // First, check that the user exists
-        try
+        IoSession ioSession = ( ( DefaultCoreSession ) userSession ).getIoSession();
+
+        if ( newPassword == null )
         {
-            Entry userEntry = userSession.lookup( userDn, SchemaConstants.ALL_ATTRIBUTES_ARRAY );
-            
-            if ( userEntry == null )
-            {
-                LOG.error( "Cannot find an entry for DN {}", userDn );
-                // We can't find the entry in the DIT
-                ioPipe.write( new PasswordModifyResponseImpl(
-                    req.getMessageId(), ResultCodeEnum.NO_SUCH_OBJECT, "Cannot find an entry for DN " + userDn ) );
+            // We don't support password generation on ApacheDS
+            writeResult( ioSession, req, ResultCodeEnum.UNWILLING_TO_PERFORM, 
+                "Cannot change a password for user " + userDn + ", exception : null new password" );
 
-                return;
+            return;
+        }
+        
+        // Get the user password attribute
+        Attribute userPassword = userEntry.get( SchemaConstants.USER_PASSWORD_AT );
+        
+        if ( userPassword == null )
+        {
+            // We can't modify the password
+            writeResult( ioSession, req, ResultCodeEnum.UNWILLING_TO_PERFORM, 
+                "Cannot change a password for user " + userDn + ", the user has no existing password" );
+
+            return;
+        }
+        
+        if ( userPassword.contains( newPassword ) )
+        {
+           // Ok, we are done : just return success
+            PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
+                req.getMessageId(), ResultCodeEnum.SUCCESS );
+
+            Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
+
+            if ( ppolicyControl != null )
+            {
+                pmrl.addControl( ppolicyControl );
             }
+
+            ioSession.write( pmrl );
+
+            return;
+        }
+        
+        if ( oldPassword == null )
+        {
+            // We are modifying the password on behalf of another user. ACI will
+            // protect such modification if it's not allowed. In any case, we just 
+            // modify the existing userPassword attribute, adding the password
+            ModifyRequest modifyRequest = new ModifyRequestImpl();
+            modifyRequest.setName( userDn );
+
+            Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
             
-            Attribute at = userEntry.get( SchemaConstants.USER_PASSWORD_AT );
+            if ( ppolicyControl != null )
+            {
+                modifyRequest.addControl( ppolicyControl );
+            }
             
-            if ( ( oldPassword != null ) && ( at != null ) )
+            try
             {
-                for ( Value value : at )
+                Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+                    userPassword.getAttributeType(), newPassword );
+    
+                modifyRequest.addModification( modification );
+                ResultCodeEnum errorCode = null;
+                String errorMessage = null;
+
+                try
                 {
-                    byte[] bytes = value.getBytes();
-                    boolean equal = PasswordUtil.compareCredentials( oldPassword, bytes );
-                    
-                    if ( equal )
+                    userSession.modify( modifyRequest );
+
+                    if ( LOG.isDebugEnabled() )
                     {
-                        oldPassword = bytes;
+                        LOG.debug( "Password modified for user {}", userDn );
                     }
-                }
-            }
-        }
-        catch ( LdapException le )
-        {
-            LOG.error( "Cannot find an entry for DN {}, exception : {}", userDn, le.getMessage() );
-            // We can't find the entry in the DIT
-            ioPipe.write(
-                new PasswordModifyResponseImpl(
-                    req.getMessageId(), ResultCodeEnum.NO_SUCH_OBJECT, "Cannot find an entry for DN " + userDn ) );
 
-            return;
-        }
+                    // Ok, all done
+                    PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
+                        req.getMessageId(), ResultCodeEnum.SUCCESS );
 
-        // We can try to update the userPassword now
-        ModifyRequest modifyRequest = new ModifyRequestImpl();
-        modifyRequest.setName( userDn );
+                    ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
 
-        Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
-        
-        if ( ppolicyControl != null )
-        {
-            modifyRequest.addControl( ppolicyControl );
-        }
+                    if ( ppolicyControl != null )
+                    {
+                        pmrl.addControl( ppolicyControl );
+                    }
 
-        Modification modification = null;
+                    ioSession.write( pmrl );
 
-        if ( oldPassword != null )
-        {
-            modification = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE,
-                SchemaConstants.USER_PASSWORD_AT, oldPassword );
+                    return;
+                }
+                catch ( LdapOperationException loe )
+                {
+                    errorCode = loe.getResultCode();
+                    errorMessage = loe.getMessage();
+                }
+                catch ( LdapException le )
+                {
+                    // this exception means something else must be wrong
+                    errorCode = ResultCodeEnum.OTHER;
+                    errorMessage = le.getMessage();
+                }
 
-            modifyRequest.addModification( modification );
-        }
+                // We can't modify the password
+                LOG.error( "Cannot modify the password for user {}, exception : {}", userDn, errorMessage );
+                PasswordModifyResponseImpl errorPmrl = new PasswordModifyResponseImpl(
+                    req.getMessageId(), errorCode, "Cannot modify the password for user "
+                        + userDn + ", exception : " + errorMessage );
 
-        if ( newPassword != null )
-        {
-            if ( oldPassword == null )
-            {
-                modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
-                    SchemaConstants.USER_PASSWORD_AT, newPassword );
+                ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
+
+                if ( ppolicyControl != null )
+                {
+                    errorPmrl.addControl( ppolicyControl );
+                }
+
+                ioSession.write( errorPmrl );
+                
+                return;
             }
-            else
+            catch ( LdapInvalidAttributeValueException liave )
             {
-                modification = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE,
-                    SchemaConstants.USER_PASSWORD_AT, newPassword );
+                // Nothing to do, this will never be a problem
             }
-
-            modifyRequest.addModification( modification );
         }
         else
         {
-            // In this case, we could either generate a new password, or return an error
-            // Atm, we will return an unwillingToPerform error
-            LOG.error( "Cannot create a new password for user {}, exception : null new password", userDn );
+            // We are changing the password of the current user, check the password
+            boolean valid = false;
+            Attribute modifiedPassword = new DefaultAttribute( userPassword.getAttributeType() );
+            
+            for ( Value value : userPassword )
+            {
+                if ( !valid )
+                {
+                    valid = PasswordUtil.compareCredentials( oldPassword, value.getBytes() );
+                }
+                
+                try
+                {
+                    if ( valid )
+                    {
+                        modifiedPassword.add( newPassword );
+                    }
+                    else
+                    { 
+                        modifiedPassword.add( value );
+                    }
+                }
+                catch ( LdapInvalidAttributeValueException e )
+                {
+                    // Nothing to do, this will never be a problem
+                }
+            }
+            
+            // At this point, we have what is needed to modify the password, if the oldPassword
+            // was valid
+            if ( valid )
+            {
+                ModifyRequest modifyRequest = new ModifyRequestImpl();
+                modifyRequest.setName( userDn );
 
-            // We can't modify the password
-            ioPipe.write( new PasswordModifyResponseImpl(
-                req.getMessageId(), ResultCodeEnum.UNWILLING_TO_PERFORM, "Cannot generate a new password for user "
-                    + userDn ) );
+                Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
+                
+                if ( ppolicyControl != null )
+                {
+                    modifyRequest.addControl( ppolicyControl );
+                }
+                
+                Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+                    modifiedPassword );
 
-            return;
-        }
+                modifyRequest.addModification( modification );
 
-        ResultCodeEnum errorCode = null;
-        String errorMessage = null;
+                ResultCodeEnum errorCode = null;
+                String errorMessage = null;
 
-        try
-        {
-            userSession.modify( modifyRequest );
+                try
+                {
+                    userSession.modify( modifyRequest );
 
-            if ( LOG.isDebugEnabled() )
-            {
-                LOG.debug( "Password modified for user {}", userDn );
-            }
+                    if ( LOG.isDebugEnabled() )
+                    {
+                        LOG.debug( "Password modified for user {}", userDn );
+                    }
 
-            // Ok, all done
-            PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
-                req.getMessageId(), ResultCodeEnum.SUCCESS );
+                    // Ok, all done
+                    PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
+                        req.getMessageId(), ResultCodeEnum.SUCCESS );
 
-            ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
+                    ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
 
-            if ( ppolicyControl != null )
+                    if ( ppolicyControl != null )
+                    {
+                        pmrl.addControl( ppolicyControl );
+                    }
+
+                    ioSession.write( pmrl );
+
+                    return;
+                }
+                catch ( LdapOperationException loe )
+                {
+                    errorCode = loe.getResultCode();
+                    errorMessage = loe.getMessage();
+                }
+                catch ( LdapException le )
+                {
+                    // this exception means something else must be wrong
+                    errorCode = ResultCodeEnum.OTHER;
+                    errorMessage = le.getMessage();
+                }
+
+                // We can't modify the password
+                LOG.error( "Cannot modify the password for user {}, exception : {}", userDn, errorMessage );
+                PasswordModifyResponseImpl errorPmrl = new PasswordModifyResponseImpl(
+                    req.getMessageId(), errorCode, "Cannot modify the password for user "
+                        + userDn + ", exception : " + errorMessage );
+
+                ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
+
+                if ( ppolicyControl != null )
+                {
+                    errorPmrl.addControl( ppolicyControl );
+                }
+
+                ioSession.write( errorPmrl );
+                
+                return;
+            }
+            else
             {
-                pmrl.addControl( ppolicyControl );
+                // Too bad, the old password is invalid
+                writeResult( ioSession, req, ResultCodeEnum.INVALID_CREDENTIALS, 
+                    "Cannot change a password for user " + userDn + ", invalid credentials" );
+
+                return;
             }
+        }
+    }
 
-            ioPipe.write( pmrl );
+    
+    private void writeResult( LdapSession requestor, PasswordModifyRequest req, ResultCodeEnum error, String errorMessage )
+    {
+        writeResult( requestor.getIoSession(), req, error, errorMessage );
 
-            return;
-        }
-        catch ( LdapOperationException loe )
+    }
+
+    
+    private void writeResult( IoSession ioSession, PasswordModifyRequest req, ResultCodeEnum error, String errorMessage )
+    {
+        LOG.error( errorMessage );
+        ioSession.write( new PasswordModifyResponseImpl(
+            req.getMessageId(), error, errorMessage ) );
+
+    }
+    
+    
+    private Entry getModifiedEntry( LdapSession requestor, PasswordModifyRequest req, Dn entryDn )
+    {
+        try
         {
-            errorCode = loe.getResultCode();
-            errorMessage = loe.getMessage();
+            Entry modifiedEntry = requestor.getCoreSession().lookup( entryDn, SchemaConstants.ALL_ATTRIBUTES_ARRAY );
+            
+            if ( modifiedEntry == null )
+            {
+                // The entry does not exist, we can't modify its password
+                writeResult( requestor, req, ResultCodeEnum.NO_SUCH_OBJECT, 
+                    "The entry does not exist, we can't modify its password" );
+                return null;
+            }
+            else
+            {
+                return modifiedEntry;
+            }
         }
-        catch ( LdapException le )
+        catch ( Exception le )
         {
-            // this exception means something else must be wrong
-            errorCode = ResultCodeEnum.OTHER;
-            errorMessage = le.getMessage();
+            // The entry does not exist, we can't modify its password
+            writeResult( requestor, req, ResultCodeEnum.NO_SUCH_OBJECT, 
+                "The entry does not exist, we can't modify its password" );
+            return null;
         }
+    }
+    
+    
+    private void processAuthenticatedPasswordModify( LdapSession requestor, PasswordModifyRequest req,
+        Dn userDn )
+    {
+        byte[] oldPassword = req.getOldPassword();
+        byte[] newPassword = req.getNewPassword();
 
-        // We can't modify the password
-        LOG.error( "Cannot modify the password for user {}, exception : {}", userDn, errorMessage );
-        PasswordModifyResponseImpl errorPmrl = new PasswordModifyResponseImpl(
-            req.getMessageId(), errorCode, "Cannot modify the password for user "
-                + userDn + ", exception : " + errorMessage );
-
-        ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
+        // We are already bound. Fetch the entry which we want to modify
+        Entry modifiedEntry = null;
+        
+        Dn principalDn = requestor.getCoreSession().getEffectivePrincipal().getDn();
 
-        if ( ppolicyControl != null )
+        LOG.debug( "User {} trying to modify password of user {}", principalDn, userDn );
+        
+        
+        // First, check that the userDn is null : we can't change the password of someone else
+        // except if we are admin
+        if ( ( userDn != null ) && ( !userDn.equals( principalDn ) ) )
         {
-            errorPmrl.addControl( ppolicyControl );
+            // Are we admin ?
+            if ( requestor.getCoreSession().isAdministrator() )
+            {
+                modifiedEntry = getModifiedEntry( requestor, req, userDn );
+                
+                if ( modifiedEntry == null )
+                {
+                    return;
+                }
+                
+                // We are administrator, we can try to modify the user's credentials
+                modifyUserPassword( requestor.getCoreSession(), modifiedEntry, userDn, oldPassword, newPassword, req );
+            }
+            else
+            {
+                // No : error
+                writeResult( requestor, req, ResultCodeEnum.INSUFFICIENT_ACCESS_RIGHTS, 
+                    "Non-admin user cannot access another user's password to modify it" );
+            }
         }
+        else
+        {
+            // We are trying to modify our own password
+            modifiedEntry = getModifiedEntry( requestor, req, principalDn );
 
-        ioPipe.write( errorPmrl );
-    }
+            if ( modifiedEntry == null )
+            {
+                return;
+            }
 
+            modifyUserPassword( requestor.getCoreSession(), modifiedEntry, principalDn, oldPassword, newPassword, req );
+        }
+    }
+    
 
     /**
      * {@inheritDoc}
@@ -261,10 +462,10 @@ public class PwdModifyHandler implements ExtendedOperationHandler<PasswordModify
             }
             catch ( LdapInvalidDnException lide )
             {
-                LOG.error( "The user DN is invalid : {}", userDn );
                 // The userIdentity is not a DN : return with an error code.
-                requestor.getIoSession().write( new PasswordModifyResponseImpl(
-                    req.getMessageId(), ResultCodeEnum.INVALID_DN_SYNTAX, "The user DN is invalid : " + userDn ) );
+                writeResult( requestor, req, ResultCodeEnum.INVALID_DN_SYNTAX, 
+                    "The user DN is invalid : " + userDn );
+
                 return;
             }
         }
@@ -275,34 +476,7 @@ public class PwdModifyHandler implements ExtendedOperationHandler<PasswordModify
         // First check if the user is bound or not
         if ( requestor.isAuthenticated() )
         {
-            Dn principalDn = requestor.getCoreSession().getEffectivePrincipal().getDn();
-
-            LOG.debug( "User {} trying to modify password of user {}", principalDn, userDn );
-
-            // First, check that the userDn is null : we can't change the password of someone else
-            // except if we are admin
-            if ( ( userDn != null ) && ( !userDn.equals( principalDn ) ) )
-            {
-                // Are we admin ?
-                if ( !requestor.getCoreSession().isAdministrator() )
-                {
-                    // No : error
-                    LOG.error( "Non-admin user cannot access another user's password to modify it" );
-                    requestor.getIoSession().write( new PasswordModifyResponseImpl(
-                        req.getMessageId(), ResultCodeEnum.INSUFFICIENT_ACCESS_RIGHTS,
-                        "Non-admin user cannot access another user's password to modify it" ) );
-                }
-                else
-                {
-                    // We are administrator, we can try to modify the user's credentials
-                    modifyUserPassword( requestor.getCoreSession(), requestor.getIoSession(), userDn, oldPassword, newPassword, req );
-                }
-            }
-            else
-            {
-                // We are trying to modify our own password
-                modifyUserPassword( requestor.getCoreSession(), requestor.getIoSession(), principalDn, oldPassword, newPassword, req );
-            }
+            processAuthenticatedPasswordModify( requestor, req, userDn );
         }
         else
         {
@@ -328,7 +502,7 @@ public class PwdModifyHandler implements ExtendedOperationHandler<PasswordModify
 
             // Ok, we were able to bind using the userIdentity and the password. Let's
             // modify the password now
-            modifyUserPassword( requestor.getCoreSession(), requestor.getIoSession(), userDn, oldPassword, newPassword, req );
+            modifyUserPassword( requestor.getCoreSession(), bindContext.getEntry(), userDn, oldPassword, newPassword, req );
         }
     }
 
diff --git a/server-integ/src/test/java/org/apache/directory/server/operations/extended/PwdModifyIT.java b/server-integ/src/test/java/org/apache/directory/server/operations/extended/PwdModifyIT.java
index 82d48f2..0ec2599 100644
--- a/server-integ/src/test/java/org/apache/directory/server/operations/extended/PwdModifyIT.java
+++ b/server-integ/src/test/java/org/apache/directory/server/operations/extended/PwdModifyIT.java
@@ -64,6 +64,7 @@ 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.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -83,6 +84,7 @@ import org.junit.runner.RunWith;
 //disable changelog, for more info see DIRSERVER-1528
 @CreateDS(enableChangeLog = false, name = "PasswordPolicyTest", additionalInterceptors =
     { Sha512PasswordHashingInterceptor.class })
+@Ignore
 public class PwdModifyIT extends AbstractLdapTestUnit
 {
     private static final PasswordPolicyResponse PP_REQ_CTRL = new PasswordPolicyResponseImpl();
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..29607d2
--- /dev/null
+++ b/server-integ/src/test/java/org/apache/directory/server/operations/extended/PwdModifyNoPPNoPasswordHashingIT.java
@@ -0,0 +1,1828 @@
+/*
+ *  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, when no PasswordPolicy or PasswordHashing interceptor
+ * are present.
+ * 
+ * @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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Bind as the user
+            Entry user;
+            
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" ) )
+            {
+                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
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser( adminConnection, "User1", "secret1" );
+        
+            // Bind as the user
+            try ( 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
+            try ( LdapConnection 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() );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Bind as the user
+            try ( 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 ( LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" ) )
+            {
+                
+                fail();
+            }
+            catch ( LdapAuthenticationException lae )
+            {
+                // We are fine
+            }
+    
+            // Rebind with the original password
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser( adminConnection, "User1", "secret1" );
+        
+            // Bind as the user
+            try ( 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
+            try ( LdapConnection  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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser( adminConnection, "User1", "secret1" );
+    
+            // Bind as the user
+            try ( 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
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser( adminConnection, "User1", "secret1" );
+
+            // Bind as the user
+            try ( 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
+            try ( LdapConnection 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() );
+            }
+        }
+    }
+
+
+    //-----------------------------------------------------------------------------------
+    // 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+            // Bind as the user
+            try ( 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
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+    
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+            // Bind as the user
+            Entry user;
+            
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" ) )
+            {
+                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 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() );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+            // Bind as the user
+            Entry user;
+            
+            try ( LdapConnection userConnection = getNetworkConnectionAs( getLdapServer(), "cn=user1,ou=system", "secret1" ) )
+            {
+                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
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+            // Bind as the user
+            try ( 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
+            try ( LdapConnection 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() );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+            // Bind as the user
+            try ( 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 ( LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" ) )
+            {
+                fail();
+            }
+            catch ( LdapAuthenticationException lae )
+            {
+                // We are fine
+            }
+    
+            // Rebind with the original password
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+    
+            // Bind as the user
+            try ( 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
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+    
+            // Bind as the user
+            try ( 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
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+    
+    
+    //-----------------------------------------------------------------------------------
+    // Non connected user
+    //-----------------------------------------------------------------------------------
+    /**
+     * Modify an existing user password while the user is not connected
+     */
+    @Test
+    public void testModifyUserPasswordAnonymous() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser( adminConnection, "User2", "secret2" );
+    
+            try ( LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User2,ou=system", "secret2" ) )
+            {
+                Entry entry = userConnection.lookup( "cn=User2,ou=system" );
+        
+                assertNotNull( entry );
+            }
+    
+            // Anonymous Bind
+            try ( 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
+            try ( LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User2,ou=system", "secret2Bis" ) )
+            { 
+                Entry entry = userConnection.lookup( "cn=User2,ou=system" );
+        
+                assertNotNull( entry );
+            }
+        }
+    }
+    
+    
+    /**
+     * Test that the server refuse to generate a password
+     */
+    @Test
+    public void testOwnGenPassword() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser( adminConnection, "User6", "secret6" );
+    
+            // Modify the user with the user account
+            try ( 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() );
+            }
+        }
+    }
+
+
+    //-----------------------------------------------------------------------------------
+    // With admin
+    //-----------------------------------------------------------------------------------
+    /**
+     * Modify an existing user password with an admin account
+     */
+    @Test
+    public void testAdminModifyPassword() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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" ) );
+        }
+    }
+
+
+    /**
+     * 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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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 );
+            
+                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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            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" ) );
+            }
+        }
+    }
+    //-----------------------------------------------------------------------------------
+    // Admin password modification with two passwords
+    //-----------------------------------------------------------------------------------
+    /**
+     * Modify an existing user password with an admin account:
+     * 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 two values
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testAdminPasswordModify2PasswordsNoOldNoNew() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+    
+            // 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() );
+    
+            // Rebind with the original password
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+    
+    /**
+     * Modify an existing user password with an admin account:
+     * o the userIdentity is 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 testAdminPasswordModify2PassworsdNoOldNew() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+
+            Entry user = adminConnection.lookup( "cn=User1,ou=system", "userPassword", "modifyTimestamp" );
+
+            // 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() );
+    
+            // Now try to bind with the new password
+            try ( LdapConnection 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() );
+                assertFalse( user.containsAttribute( "modifyTimestamp" ) );
+                assertNotNull( entry.get( "modifyTimestamp" ).getString() );
+            } 
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with an admin account:
+     * o the userIdentity is 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 testAdminPasswordModify2PasswordsNoOldNewExists() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+    
+            Entry user = adminConnection.lookup( "cn=user1,ou=system", "modifyTimestamp" );
+            
+            // 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() );
+    
+            // Now try to bind with the new password
+            try ( LdapConnection 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" ) );
+            }                
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with an admin account:
+     * o the userIdentity is 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 testAdminPasswordModify2PasswordsOldNew() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+    
+            // 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() );
+    
+            // Now try to bind with the new password
+            try ( LdapConnection 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() );
+                assertTrue( entry.containsAttribute( "modifyTimestamp" ) );
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with an admin account:
+     * o the userIdentity is 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 testAdminPasswordModify2PasswordsInvalidOldNew() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+    
+            // 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( "secret1Bis" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.INVALID_CREDENTIALS, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            // Now try to bind with the new password. Should fail
+            try
+            {
+                try ( LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User1,ou=system", "secret1Bis" ) )
+                {
+                    
+                }
+                fail();
+            }
+            catch ( LdapAuthenticationException lae )
+            {
+                // We are fine
+            }
+    
+            // Rebind with the original password
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with an admin account:
+     * 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 two values
+     * 
+     * At the same time, PP and passwordHashing interceptor are disabled
+     */
+    @Test
+    public void testAdminPasswordModify2PasswordsOldNoNew() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+    
+            // 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() );
+    
+            // Rebind with the original password
+            try ( LdapConnection 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" ) );
+            }        
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with an admin account:
+     * o the userIdentity is 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 testAdminPasswordModify2PasswordsOldNewExists() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        {
+            // Create a user
+            addUser2Passwords( adminConnection, "User1", "secret1", "other" );
+    
+            // 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( "other" ) );
+    
+            // Send the request
+            PasswordModifyResponse pwdModifyResponse = ( PasswordModifyResponse ) adminConnection.extended( pwdModifyRequest );
+    
+            assertEquals( ResultCodeEnum.SUCCESS, pwdModifyResponse.getLdapResult().getResultCode() );
+    
+            // Rebind with the original password
+            try ( LdapConnection 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" ) );
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with an admin account
+     */
+    @Test
+    public void testAdminModifyMultiplePassword() throws Exception
+    {
+        try ( LdapConnection adminConnection = getAdminNetworkConnection( getLdapServer() ) )
+        { 
+            // Create a user
+            addUser2Passwords( adminConnection, "User5", "secret51", "secret52" );
+    
+            // Modify the user with the admin account
+            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
+            try ( LdapConnection userConnection = getNetworkConnectionAs( ldapServer, "cn=User5,ou=system", "secret5Bis" ) )
+            {
+                Entry entry = userConnection.lookup( "cn=User5,ou=system" );
+        
+                assertNotNull( entry );
+        
+                userConnection.close();
+            }
+        }
+    }
+
+
+    /**
+     * Modify an existing user password with a bad account
+     */
+    @Test
+    public void testAdminModifyPasswordBadUser() throws Exception
+    {
+        try ( 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() );
+        }
+    }
+
+
+    /**
+     * Test that the server refuse to generate a password
+     */
+    @Test
+    public void testAdminGenPassword() throws Exception
+    {
+        try ( 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() );
+        }
+    }
+}