001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027
028package org.opends.guitools.controlpanel.ui;
029
030import java.awt.Component;
031import java.awt.GridBagConstraints;
032import java.net.URI;
033import java.security.cert.X509Certificate;
034import java.util.Iterator;
035import java.util.LinkedHashSet;
036
037import javax.naming.NamingException;
038import javax.naming.ldap.InitialLdapContext;
039import javax.swing.JLabel;
040import javax.swing.JPasswordField;
041import javax.swing.JTextField;
042import javax.swing.SwingUtilities;
043
044import org.forgerock.i18n.LocalizableMessage;
045import org.forgerock.i18n.slf4j.LocalizedLogger;
046import org.opends.admin.ads.util.ApplicationTrustManager;
047import org.opends.guitools.controlpanel.datamodel.ConfigReadException;
048import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
049import org.opends.guitools.controlpanel.util.BackgroundTask;
050import org.opends.guitools.controlpanel.util.Utilities;
051import org.opends.quicksetup.UserDataCertificateException;
052import org.opends.quicksetup.ui.CertificateDialog;
053import org.opends.quicksetup.util.UIKeyStore;
054import org.opends.quicksetup.util.Utils;
055import org.opends.server.types.DN;
056import org.opends.server.util.StaticUtils;
057
058import static com.forgerock.opendj.cli.Utils.*;
059
060import static org.opends.messages.AdminToolMessages.*;
061import static org.opends.messages.QuickSetupMessages.*;
062
063/**
064 * The panel that appears when the user is asked to provide authentication.
065 */
066public class LoginPanel extends StatusGenericPanel
067{
068  private static final long serialVersionUID = 5051556513294844797L;
069  private JPasswordField pwd;
070  private JTextField dn;
071  private JLabel pwdLabel;
072  private JLabel dnLabel;
073  private String usedUrl;
074
075  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
076
077  /**
078   * Default constructor.
079   *
080   */
081  public LoginPanel()
082  {
083    super();
084    createLayout();
085  }
086
087  /** {@inheritDoc} */
088  @Override
089  public LocalizableMessage getTitle()
090  {
091    return INFO_CTRL_PANEL_LOGIN_PANEL_TITLE.get();
092  }
093
094  /**
095   * Creates the layout of the panel (but the contents are not populated here).
096   */
097  private void createLayout()
098  {
099    GridBagConstraints gbc = new GridBagConstraints();
100    gbc.anchor = GridBagConstraints.WEST;
101    gbc.gridx = 0;
102    gbc.gridy = 0;
103
104    gbc.weightx = 0.0;
105    gbc.gridwidth = 1;
106    gbc.fill = GridBagConstraints.NONE;
107    dnLabel = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BIND_DN_LABEL.get());
108    add(dnLabel, gbc);
109    gbc.insets.left = 10;
110    gbc.gridx = 1;
111    dn = Utilities.createTextField("cn=Directory Manager", 20);
112    gbc.weightx = 1.0;
113    gbc.fill = GridBagConstraints.HORIZONTAL;
114    add(dn, gbc);
115    gbc.insets.top = 10;
116    gbc.insets.left = 0;
117
118    gbc.gridx = 0;
119    gbc.gridy ++;
120    gbc.weightx = 0.0;
121    gbc.gridwidth = 1;
122    gbc.fill = GridBagConstraints.NONE;
123    pwdLabel = Utilities.createPrimaryLabel(
124        INFO_CTRL_PANEL_BIND_PASSWORD_LABEL.get());
125    add(pwdLabel, gbc);
126    gbc.insets.left = 10;
127    gbc.gridx = 1;
128    pwd = Utilities.createPasswordField();
129    gbc.weightx = 1.0;
130    gbc.fill = GridBagConstraints.HORIZONTAL;
131    add(pwd, gbc);
132
133    addBottomGlue(gbc);
134  }
135
136  /** {@inheritDoc} */
137  @Override
138  public Component getPreferredFocusComponent()
139  {
140    return pwd;
141  }
142
143  /** {@inheritDoc} */
144  @Override
145  public void configurationChanged(ConfigurationChangeEvent ev)
146  {
147  }
148
149  /** {@inheritDoc} */
150  @Override
151  public void toBeDisplayed(boolean visible)
152  {
153    super.toBeDisplayed(visible);
154    if (visible)
155    {
156      pwd.setText("");
157    }
158  }
159
160  /** {@inheritDoc} */
161  @Override
162  public void okClicked()
163  {
164    setPrimaryValid(dnLabel);
165    setPrimaryValid(pwdLabel);
166    final LinkedHashSet<LocalizableMessage> errors = new LinkedHashSet<>();
167
168    boolean dnInvalid = false;
169    boolean pwdInvalid = false;
170
171    if ("".equals(dn.getText().trim()))
172    {
173      dnInvalid = true;
174      errors.add(INFO_EMPTY_DIRECTORY_MANAGER_DN.get());
175    }
176    else if (!isDN(dn.getText()))
177    {
178      dnInvalid = true;
179      errors.add(INFO_NOT_A_DIRECTORY_MANAGER_DN.get());
180    }
181
182    if (pwd.getPassword().length == 0)
183    {
184      pwdInvalid = true;
185      errors.add(INFO_EMPTY_PWD.get());
186    }
187    if (dnInvalid)
188    {
189      setPrimaryInvalid(dnLabel);
190    }
191
192    if (pwdInvalid)
193    {
194      setPrimaryInvalid(pwdLabel);
195    }
196
197    if (errors.isEmpty())
198    {
199      setEnabledOK(false);
200      setEnabledCancel(false);
201      displayMessage(INFO_CTRL_PANEL_VERIFYING_AUTHENTICATION_SUMMARY.get());
202
203      BackgroundTask<InitialLdapContext> worker =
204        new BackgroundTask<InitialLdapContext>()
205      {
206        /** {@inheritDoc} */
207        @Override
208        public InitialLdapContext processBackgroundTask() throws Throwable
209        {
210          InitialLdapContext ctx = null;
211          try
212          {
213            usedUrl = getInfo().getAdminConnectorURL();
214            ctx = Utilities.getAdminDirContext(getInfo(), dn.getText(),
215                String.valueOf(pwd.getPassword()));
216
217            if (getInfo().getDirContext() != null)
218            {
219              try
220              {
221                getInfo().getDirContext().close();
222              }
223              catch (Throwable t)
224              {
225              }
226            }
227            if (getInfo().getUserDataDirContext() != null)
228            {
229              try
230              {
231                getInfo().getUserDataDirContext().close();
232              }
233              catch (Throwable t)
234              {
235              }
236            }
237            try
238            {
239              Thread.sleep(500);
240            }
241            catch (Throwable t)
242            {
243            }
244            SwingUtilities.invokeLater(new Runnable()
245            {
246              @Override
247              public void run()
248              {
249                displayMessage(
250                    INFO_CTRL_PANEL_READING_CONFIGURATION_SUMMARY.get());
251              }
252            });
253            getInfo().setDirContext(ctx);
254            getInfo().setUserDataDirContext(null);
255            getInfo().regenerateDescriptor();
256            return ctx;
257          } catch (Throwable t)
258          {
259            StaticUtils.close(ctx);
260            throw t;
261          }
262        }
263
264        /** {@inheritDoc} */
265        @Override
266        public void backgroundTaskCompleted(InitialLdapContext ctx,
267            Throwable throwable)
268        {
269          boolean handleCertificateException = false;
270          if (throwable != null)
271          {
272            logger.info(LocalizableMessage.raw("Error connecting: " + throwable, throwable));
273
274            if (isCertificateException(throwable))
275            {
276              ApplicationTrustManager.Cause cause =
277                getInfo().getTrustManager().getLastRefusedCause();
278
279              logger.info(LocalizableMessage.raw("Certificate exception cause: "+cause));
280              UserDataCertificateException.Type excType = null;
281              if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED)
282              {
283                excType = UserDataCertificateException.Type.NOT_TRUSTED;
284              }
285              else if (cause ==
286                ApplicationTrustManager.Cause.HOST_NAME_MISMATCH)
287              {
288                excType = UserDataCertificateException.Type.HOST_NAME_MISMATCH;
289              }
290              else
291              {
292                LocalizableMessage msg = getThrowableMsg(
293                    INFO_ERROR_CONNECTING_TO_LOCAL.get(), throwable);
294                errors.add(msg);
295              }
296
297              if (excType != null)
298              {
299                String h;
300                int p;
301                try
302                {
303                  URI uri = new URI(usedUrl);
304                  h = uri.getHost();
305                  p = uri.getPort();
306                }
307                catch (Throwable t)
308                {
309                  logger.warn(LocalizableMessage.raw(
310                      "Error parsing ldap url of ldap url.", t));
311                  h = INFO_NOT_AVAILABLE_LABEL.get().toString();
312                  p = -1;
313                }
314                UserDataCertificateException udce =
315                  new UserDataCertificateException(null,
316                      INFO_CERTIFICATE_EXCEPTION.get(h, p),
317                      throwable, h, p,
318                      getInfo().getTrustManager().getLastRefusedChain(),
319                      getInfo().getTrustManager().getLastRefusedAuthType(),
320                      excType);
321
322                handleCertificateException(udce);
323                handleCertificateException = true;
324              }
325            }
326            else if (throwable instanceof NamingException)
327            {
328              boolean found = false;
329              String providedDn = dn.getText();
330              Iterator<DN> it = getInfo().getServerDescriptor().
331              getAdministrativeUsers().iterator();
332              while (it.hasNext() && !found)
333              {
334                found = Utils.areDnsEqual(providedDn, it.next().toString());
335              }
336              if (!found)
337              {
338                errors.add(INFO_NOT_A_DIRECTORY_MANAGER_IN_CONFIG.get());
339              }
340              else
341              {
342                errors.add(Utils.getMessageForException(
343                    (NamingException)throwable));
344              }
345
346              setPrimaryInvalid(dnLabel);
347              setPrimaryInvalid(pwdLabel);
348            }
349            else if (throwable instanceof ConfigReadException)
350            {
351              errors.add(((ConfigReadException)throwable).getMessageObject());
352            }
353            else
354            {
355              // This is a bug
356              throwable.printStackTrace();
357              errors.add(getThrowableMsg(INFO_BUG_MSG.get(), throwable));
358            }
359          }
360          displayMainPanel();
361          setEnabledCancel(true);
362          setEnabledOK(true);
363          if (!errors.isEmpty())
364          {
365            displayErrorDialog(errors);
366            pwd.setSelectionStart(0);
367            pwd.setSelectionEnd(pwd.getPassword().length);
368            pwd.requestFocusInWindow();
369          }
370          else if (!handleCertificateException)
371          {
372            Utilities.getParentDialog(LoginPanel.this).setVisible(false);
373          }
374        }
375      };
376      worker.startBackgroundTask();
377    }
378    else
379    {
380      displayErrorDialog(errors);
381      if (dnInvalid)
382      {
383        dn.setSelectionStart(0);
384        dn.setSelectionEnd(dn.getText().length());
385        dn.requestFocusInWindow();
386      }
387      if (pwdInvalid)
388      {
389        pwd.setSelectionStart(0);
390        pwd.setSelectionEnd(pwd.getPassword().length);
391        pwd.requestFocusInWindow();
392      }
393
394    }
395  }
396
397  /** {@inheritDoc} */
398  @Override
399  public void cancelClicked()
400  {
401    setPrimaryValid(dnLabel);
402    setPrimaryValid(pwdLabel);
403    pwd.setText(null);
404    super.cancelClicked();
405  }
406
407  /**
408   * Displays a dialog asking the user to accept a certificate if the user
409   * accepts it, we update the trust manager and simulate a click on "OK" to
410   * re-check the authentication.
411   * This method assumes that we are being called from the event thread.
412   */
413  private void handleCertificateException(UserDataCertificateException ce)
414  {
415    CertificateDialog dlg = new CertificateDialog(null, ce);
416    dlg.pack();
417    Utilities.centerGoldenMean(dlg, Utilities.getParentDialog(this));
418    dlg.setVisible(true);
419    if (dlg.getUserAnswer() !=
420      CertificateDialog.ReturnType.NOT_ACCEPTED)
421    {
422      X509Certificate[] chain = ce.getChain();
423      String authType = ce.getAuthType();
424      String host = ce.getHost();
425
426      if (chain != null && authType != null && host != null)
427      {
428        logger.info(LocalizableMessage.raw("Accepting certificate presented by host "+host));
429        getInfo().getTrustManager().acceptCertificate(chain, authType, host);
430        /* Simulate a click on the OK by calling in the okClicked method. */
431        SwingUtilities.invokeLater(new Runnable()
432        {
433          @Override
434          public void run()
435          {
436            okClicked();
437          }
438        });
439      }
440      else
441      {
442        if (chain == null)
443        {
444          logger.warn(LocalizableMessage.raw(
445              "The chain is null for the UserDataCertificateException"));
446        }
447        if (authType == null)
448        {
449          logger.warn(LocalizableMessage.raw(
450              "The auth type is null for the UserDataCertificateException"));
451        }
452        if (host == null)
453        {
454          logger.warn(LocalizableMessage.raw(
455              "The host is null for the UserDataCertificateException"));
456        }
457      }
458    }
459    if (dlg.getUserAnswer() ==
460      CertificateDialog.ReturnType.ACCEPTED_PERMANENTLY)
461    {
462      X509Certificate[] chain = ce.getChain();
463      if (chain != null)
464      {
465        try
466        {
467          UIKeyStore.acceptCertificate(chain);
468        }
469        catch (Throwable t)
470        {
471          logger.warn(LocalizableMessage.raw("Error accepting certificate: "+t, t));
472        }
473      }
474    }
475  }
476}