mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

jvergara
09.47.2009 7f207072519d5a4551dc943791cf23fef968cae7
Fix for issue 3698 (Cannot change Directory Manager password with the Control Panel)

The fix consists on using the ldappasswordmodify class to modify the password of the user and to include the current password when the password that is being modified is the one of the user we are bound with.

Using ldappasswordmodify also in the equivalent command-line (which is better than using ldapmodify to modify the password).

The code has also been updated to handle the fact that the password of the user that is being used to configure/manage the OpenDS instance has changed (even after a temporary broken connection to the server, the user will not have to provide it again).
5 files modified
313 ■■■■ changed files
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/browser/LDAPConnectionPool.java 2 ●●● patch | view | raw | blame | history
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/ModifyEntryTask.java 72 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/ResetUserPasswordTask.java 190 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/Task.java 10 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/ui/StatusGenericPanel.java 39 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/browser/LDAPConnectionPool.java
@@ -180,7 +180,7 @@
   * If authentication data available for this protocol/host/port,
   * getConnection() call bind() on the new connection.
   * If connect() or bind() failed, getConnection() forward the
   * LDAPException.
   * NamingException.
   * When getConnection() succeeds, the returned connection must
   * be passed to releaseConnection() after use.
   * @param ldapUrl the LDAP URL to which the connection must connect.
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/ModifyEntryTask.java
@@ -55,6 +55,7 @@
import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
import org.opends.guitools.controlpanel.ui.ProgressDialog;
import org.opends.guitools.controlpanel.ui.StatusGenericPanel;
import org.opends.guitools.controlpanel.ui.ViewEntryPanel;
import org.opends.guitools.controlpanel.ui.nodes.BasicNode;
import org.opends.guitools.controlpanel.util.Utilities;
@@ -84,6 +85,7 @@
  private CustomSearchResult oldEntry;
  private DN oldDn;
  private ArrayList<ModificationItem> modifications;
  private ModificationItem passwordModification;
  private Entry newEntry;
  private BrowserController controller;
  private TreePath treePath;
@@ -132,8 +134,22 @@
          ode);
    }
    modifications = getModifications(newEntry, oldEntry, getInfo());
    // Find password modifications
    for (ModificationItem mod : modifications)
    {
      if (mod.getAttribute().getID().equalsIgnoreCase("userPassword"))
      {
        passwordModification = mod;
        break;
      }
    }
    if (passwordModification != null)
    {
      modifications.remove(passwordModification);
    }
    hasModifications = modifications.size() > 0 ||
    !oldDn.equals(newEntry.getDN());
    !oldDn.equals(newEntry.getDN()) ||
    (passwordModification != null);
  }
  /**
@@ -287,6 +303,60 @@
  }
  /**
   * {@inheritDoc}
   */
  public void postOperation()
  {
    if ((lastException == null) && (state == State.FINISHED_SUCCESSFULLY) &&
        (passwordModification != null))
    {
      try
      {
        Object o = passwordModification.getAttribute().get();
        String sPwd;
        if (o instanceof byte[])
        {
          try
          {
            sPwd = new String((byte[])o, "UTF-8");
          }
          catch (Throwable t)
          {
            throw new IllegalStateException("Unexpected error: "+t, t);
          }
        }
        else
        {
          sPwd = String.valueOf(o);
        }
        ResetUserPasswordTask newTask = new ResetUserPasswordTask(getInfo(),
            getProgressDialog(), (BasicNode)treePath.getLastPathComponent(),
            controller, sPwd.toCharArray());
        if ((modifications.size() > 0) || mustRename)
        {
          getProgressDialog().appendProgressHtml("<br><br>");
        }
        StatusGenericPanel.launchOperation(newTask,
            INFO_CTRL_PANEL_RESETTING_USER_PASSWORD_SUMMARY.get(),
            INFO_CTRL_PANEL_RESETTING_USER_PASSWORD_SUCCESSFUL_SUMMARY.get(),
            INFO_CTRL_PANEL_RESETTING_USER_PASSWORD_SUCCESSFUL_DETAILS.get(),
            ERR_CTRL_PANEL_RESETTING_USER_PASSWORD_ERROR_SUMMARY.get(),
            ERR_CTRL_PANEL_RESETTING_USER_PASSWORD_ERROR_DETAILS.get(),
            null,
            getProgressDialog(),
            false,
            getInfo());
        getProgressDialog().setVisible(true);
      }
      catch (NamingException ne)
      {
        // This should not happen
        throw new IllegalStateException("Unexpected exception: "+ne, ne);
      }
    }
  }
  /**
   * Modifies and renames the entry.
   * @param ctx the connection to the server.
   * @param oldDN the oldDN of the entry.
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/ResetUserPasswordTask.java
@@ -35,27 +35,25 @@
import java.util.Set;
import java.util.TreeSet;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;
import org.opends.admin.ads.util.ConnectionUtils;
import org.opends.guitools.controlpanel.browser.BrowserController;
import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
import org.opends.guitools.controlpanel.ui.ProgressDialog;
import org.opends.guitools.controlpanel.ui.nodes.BasicNode;
import org.opends.guitools.controlpanel.ui.nodes.BrowserNodeInfo;
import org.opends.guitools.controlpanel.util.Utilities;
import org.opends.messages.Message;
import org.opends.server.config.ConfigConstants;
import org.opends.server.tools.LDAPPasswordModify;
import org.opends.server.types.DN;
import org.opends.server.types.OpenDsException;
import org.opends.server.util.ServerConstants;
import org.opends.server.util.cli.CommandBuilder;
/**
 * The task called when we want to reset the password of the user.
@@ -65,8 +63,8 @@
{
  private Set<String> backendSet;
  private BasicNode node;
  private char[] currentPassword;
  private char[] newPassword;
  private BrowserController controller;
  private DN dn;
  private boolean useAdminCtx;
@@ -86,7 +84,6 @@
    backendSet = new HashSet<String>();
    this.node = node;
    this.newPassword = pwd;
    this.controller = controller;
    try
    {
      dn = DN.decode(node.getDN());
@@ -105,6 +102,19 @@
    {
      throw new IllegalStateException("Could not parse DN: "+node.getDN(), ode);
    }
    try
    {
      InitialLdapContext ctx =
        controller.findConnectionForDisplayedEntry(node);
      if ((ctx != null) && isBoundAs(dn, ctx))
      {
        currentPassword = ConnectionUtils.getBindPassword(ctx).toCharArray();
      }
    }
    catch (Throwable t)
    {
    }
    useAdminCtx = controller.isConfigurationNode(node);
  }
  /**
@@ -145,7 +155,7 @@
   */
  protected String getCommandLinePath()
  {
    return null;
    return getCommandLinePath("ldappasswordmodify");
  }
  /**
@@ -153,7 +163,22 @@
   */
  protected ArrayList<String> getCommandLineArguments()
  {
    return new ArrayList<String>();
    ArrayList<String> args = new ArrayList<String>();
    if (currentPassword == null)
    {
      args.add("--authzID");
      args.add("dn:"+dn);
    }
    else
    {
      args.add("--currentPassword");
      args.add(String.valueOf(currentPassword));
    }
    args.add("--newPassword");
    args.add(String.valueOf(newPassword));
    args.addAll(getConnectionCommandLineArguments(useAdminCtx, true));
    args.add(getNoPropertiesFileArgument());
    return args;
  }
  /**
@@ -191,57 +216,40 @@
  {
    state = State.RUNNING;
    lastException = null;
    try
    {
      useAdminCtx = controller.isConfigurationNode(node);
      InitialLdapContext ctx =
        controller.findConnectionForDisplayedEntry(node);
      BasicAttribute attr =
        new BasicAttribute(ServerConstants.ATTR_USER_PASSWORD);
      attr.add(new String(newPassword));
      ModificationItem mod =
        new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
      ModificationItem[] mods = {mod};
      final ArrayList<ModificationItem> modifications =
        new ArrayList<ModificationItem>();
      modifications.add(mod);
      ArrayList<String> arguments = getCommandLineArguments();
      SwingUtilities.invokeLater(new Runnable()
      String[] args = new String[arguments.size()];
      arguments.toArray(args);
      returnCode = LDAPPasswordModify.mainPasswordModify(args, false,
            outPrintStream, errorPrintStream);
      if (returnCode != 0)
      {
        public void run()
        {
          printEquivalentCommand(dn, newPassword, useAdminCtx);
          getProgressDialog().appendProgressHtml(
              Utilities.getProgressWithPoints(
                  INFO_CTRL_PANEL_RESETTING_USER_PASSWORD.get(node.getDN()),
                  ColorAndFontConstants.progressFont));
        }
      });
      ctx.modifyAttributes(Utilities.getJNDIName(node.getDN()), mods);
      SwingUtilities.invokeLater(new Runnable()
        state = State.FINISHED_WITH_ERROR;
      }
      else
      {
        public void run()
        if ((lastException == null) && (currentPassword != null))
        {
          getProgressDialog().appendProgressHtml(
              Utilities.getProgressDone(ColorAndFontConstants.progressFont));
          TreePath treePath =
            new TreePath(controller.getTreeModel().getPathToRoot(node));
          if (treePath != null)
          // The connections must be updated, just update the environment, which
          // is what we use to clone connections and to launch scripts.
          // The environment will also be used if we want to reconnect.
          getInfo().getDirContext().addToEnvironment(
              Context.SECURITY_CREDENTIALS,
              String.valueOf(newPassword));
          if (getInfo().getUserDataDirContext() != null)
          {
            BrowserNodeInfo nodeInfo = controller.getNodeInfoFromPath(treePath);
            if (nodeInfo != null)
            {
              controller.notifyEntryChanged(nodeInfo);
            }
            controller.getTree().removeSelectionPath(treePath);
            controller.getTree().setSelectionPath(treePath);
            getInfo().getUserDataDirContext().addToEnvironment(
                Context.SECURITY_CREDENTIALS,
                String.valueOf(newPassword));
          }
        }
      });
      state = State.FINISHED_SUCCESSFULLY;
        state = State.FINISHED_SUCCESSFULLY;
      }
    }
    catch (Throwable t)
    {
@@ -251,31 +259,59 @@
  }
  /**
   * Prints the equivalent modify command line in the progress dialog.
   * @param dn the dn of the modified entry.
   * @param newPassword the new password.
   * @param useAdminCtx use the administration connector.
   * Returns <CODE>true</CODE> if we are bound using the provided entry.  In
   * the case of root entries this is not necessarily the same as using that
   * particular DN (we might be binding using a value specified in
   * ds-cfg-alternate-bind-dn).
   * @param dn the DN.
   * @param ctx the connection that we are using to modify the password.
   * @return <CODE>true</CODE> if we are bound using the provided entry.
   */
  private void printEquivalentCommand(DN dn, char[] newPassword,
      boolean useAdminCtx)
  private boolean isBoundAs(DN dn, InitialLdapContext ctx)
  {
    ArrayList<String> args = new ArrayList<String>();
    args.add(getCommandLinePath("ldappasswordmodify"));
    args.add("--authzID");
    args.add("dn:"+dn);
    args.add("--newPassword");
    args.add(Utilities.OBFUSCATED_VALUE);
    args.addAll(getObfuscatedCommandLineArguments(
        getConnectionCommandLineArguments(useAdminCtx, true)));
    args.add(getNoPropertiesFileArgument());
    StringBuilder sb = new StringBuilder();
    for (String arg : args)
    boolean isBoundAs = false;
    DN bindDN = DN.nullDN();
    try
    {
      sb.append(" "+CommandBuilder.escapeValue(arg));
      String b = ConnectionUtils.getBindDN(ctx);
      bindDN = DN.decode(b);
      isBoundAs = dn.equals(bindDN);
    }
    getProgressDialog().appendProgressHtml(Utilities.applyFont(
        INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_RESET_PASSWORD.get().toString()+
        "<br><b>"+sb.toString()+"</b><br><br>",
        ColorAndFontConstants.progressFont));
    catch (Throwable t)
    {
      // Ignore
    }
    if (!isBoundAs)
    {
      try
      {
        SearchControls ctls = new SearchControls();
        ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
        String filter =
          "(|(objectClass=*)(objectclass=ldapsubentry))";
        String attrName = ConfigConstants.ATTR_ROOTDN_ALTERNATE_BIND_DN;
        ctls.setReturningAttributes(new String[] {attrName});
        NamingEnumeration<SearchResult> entries =
          ctx.search(Utilities.getJNDIName(dn.toString()), filter, ctls);
        while (entries.hasMore())
        {
          SearchResult sr = entries.next();
          Set<String> dns = ConnectionUtils.getValues(sr, attrName);
          for (String sDn : dns)
          {
            if (bindDN.equals(DN.decode(sDn)))
            {
              isBoundAs = true;
              break;
            }
          }
        }
      }
      catch (Throwable t)
      {
      }
    }
    return isBoundAs;
  }
}
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/Task.java
@@ -657,12 +657,18 @@
  protected List<String> getObfuscatedCommandLineArguments(
      List<String> clearArgs)
  {
    String[] toObfuscate = {"--bindPassword", "--currentPassword",
        "--newPassword"};
    ArrayList<String> args = new ArrayList<String>(clearArgs);
    for (int i=1; i<args.size(); i++)
    {
      if (args.get(i-1).equalsIgnoreCase("--bindPassword"))
      for (String argName : toObfuscate)
      {
        args.set(i, Utilities.OBFUSCATED_VALUE);
        if (args.get(i-1).equalsIgnoreCase(argName))
        {
          args.set(i, Utilities.OBFUSCATED_VALUE);
          break;
        }
      }
    }
    return args;
opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/ui/StatusGenericPanel.java
@@ -1561,6 +1561,41 @@
      final MessageDescriptor.Arg1<Number> errorDetailCode,
      final ProgressDialog dialog, boolean resetLogs)
  {
    launchOperation(task, initialSummary, successSummary, successDetail,
        errorSummary, errorDetail, errorDetailCode, dialog, resetLogs,
        getInfo());
  }
  /**
   * Launch an task.
   * @param task the task to be launched.
   * @param initialSummary the initial summary to be displayed in the progress
   * dialog.
   * @param successSummary the success summary to be displayed in the progress
   * dialog if the task is successful.
   * @param successDetail the success details to be displayed in the progress
   * dialog if the task is successful.
   * @param errorSummary the error summary to be displayed in the progress
   * dialog if the task ended with error.
   * @param errorDetail error details to be displayed in the progress
   * dialog if the task ended with error.
   * @param errorDetailCode error detail message to be displayed in the progress
   * dialog if the task ended with error and we have an exit error code (for
   * instance if the error occurred when launching a script we will have an
   * error code).
   * @param dialog the progress dialog.
   * @param resetLogs whether the contents of the progress dialog should be
   * reset or not.
   * @param info the ControlPanelInfo.
   */
  public static void launchOperation(final Task task, Message initialSummary,
      final Message successSummary, final Message successDetail,
      final Message errorSummary,
      final Message errorDetail,
      final MessageDescriptor.Arg1<Number> errorDetailCode,
      final ProgressDialog dialog, boolean resetLogs,
      final ControlPanelInfo info)
  {
    dialog.setTaskIsOver(false);
    dialog.getProgressBar().setIndeterminate(true);
    dialog.addPrintStreamListeners(task.getOutPrintStream(),
@@ -1592,7 +1627,7 @@
        task.runTask();
        if (task.regenerateDescriptor())
        {
          getInfo().regenerateDescriptor();
          info.regenerateDescriptor();
        }
        return task;
      }
@@ -1676,7 +1711,7 @@
        task.postOperation();
      }
    };
    getInfo().registerTask(task);
    info.registerTask(task);
    worker.startBackgroundTask();
  }