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