Refactored the PwdModifyHandler class
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..419b301 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.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.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 @@
/**
* 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
- {
- 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 ) );
+ IoSession ioSession = ( ( DefaultCoreSession ) userSession ).getIoSession();
- return;
- }
-
- Attribute at = userEntry.get( SchemaConstants.USER_PASSWORD_AT );
-
- if ( ( oldPassword != null ) && ( at != null ) )
- {
- for ( Value value : at )
- {
- byte[] bytes = value.getBytes();
- boolean equal = PasswordUtil.compareCredentials( oldPassword, bytes );
-
- if ( equal )
- {
- oldPassword = bytes;
- }
- }
- }
- }
- catch ( LdapException le )
+ if ( newPassword == null )
{
- 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 ) );
+ // 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;
}
-
- // We can try to update the userPassword now
- ModifyRequest modifyRequest = new ModifyRequestImpl();
- modifyRequest.setName( userDn );
-
- Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
- if ( ppolicyControl != null )
+ // Get the user password attribute
+ Attribute userPassword = userEntry.get( SchemaConstants.USER_PASSWORD_AT );
+
+ if ( userPassword == null )
{
- modifyRequest.addControl( ppolicyControl );
- }
-
- Modification modification = null;
-
- if ( oldPassword != null )
- {
- modification = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE,
- SchemaConstants.USER_PASSWORD_AT, oldPassword );
-
- modifyRequest.addModification( modification );
- }
-
- if ( newPassword != null )
- {
- if ( oldPassword == null )
- {
- modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
- SchemaConstants.USER_PASSWORD_AT, newPassword );
- }
- else
- {
- modification = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE,
- SchemaConstants.USER_PASSWORD_AT, newPassword );
- }
-
- 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 can't modify the password
- ioPipe.write( new PasswordModifyResponseImpl(
- req.getMessageId(), ResultCodeEnum.UNWILLING_TO_PERFORM, "Cannot generate a new password for user "
- + userDn ) );
+ writeResult( ioSession, req, ResultCodeEnum.UNWILLING_TO_PERFORM,
+ "Cannot change a password for user " + userDn + ", the user has no existing password" );
return;
}
-
- ResultCodeEnum errorCode = null;
- String errorMessage = null;
-
- try
+
+ if ( userPassword.contains( newPassword ) )
{
- userSession.modify( modifyRequest );
-
- if ( LOG.isDebugEnabled() )
- {
- LOG.debug( "Password modified for user {}", userDn );
- }
-
- // Ok, all done
+ // Ok, we are done : just return success
PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
req.getMessageId(), ResultCodeEnum.SUCCESS );
- ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
+ Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
if ( ppolicyControl != null )
{
pmrl.addControl( ppolicyControl );
}
- ioPipe.write( pmrl );
+ ioSession.write( pmrl );
return;
}
- catch ( LdapOperationException loe )
+
+ if ( oldPassword == null )
{
- errorCode = loe.getResultCode();
- errorMessage = loe.getMessage();
+ // 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 );
+
+ if ( ppolicyControl != null )
+ {
+ modifyRequest.addControl( ppolicyControl );
+ }
+
+ try
+ {
+ Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+ userPassword.getAttributeType(), newPassword );
+
+ modifyRequest.addModification( modification );
+ ResultCodeEnum errorCode = null;
+ String errorMessage = null;
+
+ try
+ {
+ userSession.modify( modifyRequest );
+
+ if ( LOG.isDebugEnabled() )
+ {
+ LOG.debug( "Password modified for user {}", userDn );
+ }
+
+ // Ok, all done
+ PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
+ req.getMessageId(), ResultCodeEnum.SUCCESS );
+
+ ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
+
+ 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;
+ }
+ catch ( LdapInvalidAttributeValueException liave )
+ {
+ // Nothing to do, this will never be a problem
+ }
}
- catch ( LdapException le )
+ else
{
- // this exception means something else must be wrong
- errorCode = ResultCodeEnum.OTHER;
- errorMessage = le.getMessage();
+ // 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 );
+
+ Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
+
+ if ( ppolicyControl != null )
+ {
+ modifyRequest.addControl( ppolicyControl );
+ }
+
+ Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+ modifiedPassword );
+
+ modifyRequest.addModification( modification );
+
+ ResultCodeEnum errorCode = null;
+ String errorMessage = null;
+
+ try
+ {
+ userSession.modify( modifyRequest );
+
+ if ( LOG.isDebugEnabled() )
+ {
+ LOG.debug( "Password modified for user {}", userDn );
+ }
+
+ // Ok, all done
+ PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
+ req.getMessageId(), ResultCodeEnum.SUCCESS );
+
+ ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
+
+ 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
+ {
+ // Too bad, the old password is invalid
+ writeResult( ioSession, req, ResultCodeEnum.INVALID_CREDENTIALS,
+ "Cannot change a password for user " + userDn + ", invalid credentials" );
+
+ return;
+ }
}
-
- // 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 );
- }
-
- ioPipe.write( errorPmrl );
}
+
+ private void writeResult( LdapSession requestor, PasswordModifyRequest req, ResultCodeEnum error, String errorMessage )
+ {
+ writeResult( requestor.getIoSession(), req, error, errorMessage );
+
+ }
+
+
+ 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
+ {
+ 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 ( Exception le )
+ {
+ // 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 are already bound. Fetch the entry which we want to modify
+ Entry modifiedEntry = null;
+
+ 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() )
+ {
+ 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 );
+
+ if ( modifiedEntry == null )
+ {
+ return;
+ }
+
+ modifyUserPassword( requestor.getCoreSession(), modifiedEntry, principalDn, oldPassword, newPassword, req );
+ }
+ }
+
/**
* {@inheritDoc}
@@ -261,10 +462,10 @@
}
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 @@
// 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 @@
// 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 );
}
}