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