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-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2016 ForgeRock AS.
016 */
017package org.opends.quicksetup.installer.ui;
018
019import java.awt.Component;
020import java.awt.GridBagConstraints;
021import java.awt.GridBagLayout;
022import java.awt.Insets;
023import java.awt.event.ActionEvent;
024import java.awt.event.ActionListener;
025import java.awt.event.WindowAdapter;
026import java.awt.event.WindowEvent;
027import java.io.File;
028import java.security.KeyStoreException;
029import java.util.ArrayList;
030import java.util.Arrays;
031
032import javax.swing.Box;
033import javax.swing.ButtonGroup;
034import javax.swing.JButton;
035import javax.swing.JCheckBox;
036import javax.swing.JComponent;
037import javax.swing.JDialog;
038import javax.swing.JFrame;
039import javax.swing.JLabel;
040import javax.swing.JPanel;
041import javax.swing.JPasswordField;
042import javax.swing.JRadioButton;
043import javax.swing.JTextField;
044import javax.swing.SwingUtilities;
045import javax.swing.text.JTextComponent;
046
047import org.opends.quicksetup.SecurityOptions;
048import org.opends.quicksetup.event.BrowseActionListener;
049import org.opends.quicksetup.event.MinimumSizeComponentListener;
050import org.opends.quicksetup.installer.Installer;
051import org.opends.quicksetup.ui.UIFactory;
052import org.opends.quicksetup.ui.Utilities;
053import org.opends.quicksetup.util.BackgroundTask;
054import org.opends.quicksetup.util.Utils;
055import org.opends.server.util.CertificateManager;
056import org.opends.server.util.StaticUtils;
057import org.forgerock.i18n.LocalizableMessage;
058
059import static org.opends.messages.QuickSetupMessages.*;
060import static com.forgerock.opendj.cli.Utils.getThrowableMsg;
061
062/**
063 * This class is a dialog that appears when the user wants to configure
064 * security parameters for the new OpenDS instance.
065 */
066public class SecurityOptionsDialog extends JDialog
067{
068  private static final long serialVersionUID = 4083707346899442215L;
069
070  private JCheckBox cbEnableSSL;
071  private JCheckBox cbEnableStartTLS;
072  private JTextField tfPort;
073  private JRadioButton rbUseSelfSignedCertificate;
074  private JRadioButton rbUseExistingCertificate;
075  private JLabel lKeystoreType;
076  private JRadioButton rbPKCS11;
077  private JRadioButton rbJKS;
078  private JRadioButton rbJCEKS;
079  private JRadioButton rbPKCS12;
080  private JLabel lKeystorePath;
081  private JTextField tfKeystorePath;
082  private JButton browseButton;
083  private JLabel lKeystorePwd;
084  private JPasswordField tfKeystorePwd;
085
086  private JButton cancelButton;
087  private JButton okButton;
088
089  private SelectAliasDialog aliasDlg;
090
091  private boolean isCanceled = true;
092
093  private SecurityOptions securityOptions;
094
095  private String[] aliases;
096  private boolean certificateHasAlias;
097  private String selectedAlias;
098
099  private final int DEFAULT_PORT = 636;
100
101  /**
102   * Constructor of the SecurityOptionsDialog.
103   * @param parent the parent frame for this dialog.
104   * @param options the SecurityOptions used to populate this dialog.
105   * @throws IllegalArgumentException if options is null.
106   */
107  public SecurityOptionsDialog(JFrame parent, SecurityOptions options)
108  throws IllegalArgumentException
109  {
110    super(parent);
111    setTitle(INFO_SECURITY_OPTIONS_DIALOG_TITLE.get().toString());
112    securityOptions = options;
113    getContentPane().add(createPanel());
114    pack();
115
116    updateContents();
117
118    int minWidth = (int) getPreferredSize().getWidth();
119    int minHeight = (int) getPreferredSize().getHeight();
120    addComponentListener(new MinimumSizeComponentListener(this, minWidth,
121        minHeight));
122    getRootPane().setDefaultButton(okButton);
123
124    addWindowListener(new WindowAdapter()
125    {
126      @Override
127      public void windowClosing(WindowEvent e)
128      {
129        cancelClicked();
130      }
131    });
132    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
133
134    Utilities.centerOnComponent(this, parent);
135  }
136
137  /**
138   * Returns <CODE>true</CODE> if the user clicked on cancel and
139   * <CODE>false</CODE> otherwise.
140   * @return <CODE>true</CODE> if the user clicked on cancel and
141   * <CODE>false</CODE> otherwise.
142   */
143  public boolean isCanceled()
144  {
145    return isCanceled;
146  }
147
148  /**
149   * Displays this dialog and populates its contents with the provided
150   * SecurityOptions object.
151   * @param options the SecurityOptions used to populate this dialog.
152   * @throws IllegalArgumentException if options is null.
153   */
154  public void display(SecurityOptions options) throws IllegalArgumentException
155  {
156    if (options == null)
157    {
158      throw new IllegalArgumentException("options parameter cannot be null.");
159    }
160    UIFactory.setTextStyle(cbEnableSSL,
161        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
162    UIFactory.setTextStyle(lKeystorePath,
163        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
164    UIFactory.setTextStyle(lKeystorePwd,
165        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
166
167    securityOptions = options;
168    updateContents();
169
170    isCanceled = true;
171
172    setVisible(true);
173  }
174
175  /**
176   * Returns the security options object representing the input of the user
177   * in this panel.
178   * @return the security options object representing the input of the user
179   * in this panel.
180   */
181  public SecurityOptions getSecurityOptions()
182  {
183    SecurityOptions ops;
184
185    boolean enableSSL = cbEnableSSL.isSelected();
186    boolean enableStartTLS = cbEnableStartTLS.isSelected();
187    if (enableSSL || enableStartTLS)
188    {
189      int sslPort = -1;
190      try
191      {
192        sslPort = Integer.parseInt(tfPort.getText());
193      }
194      catch (Throwable t)
195      {
196      }
197      if (rbUseSelfSignedCertificate.isSelected())
198      {
199        ops = SecurityOptions.createSelfSignedCertificateOptions(
200            enableSSL, enableStartTLS, sslPort);
201      }
202      else if (rbJKS.isSelected())
203      {
204        ops = SecurityOptions.createJKSCertificateOptions(
205            tfKeystorePath.getText(),
206            String.valueOf(tfKeystorePwd.getPassword()), enableSSL,
207            enableStartTLS, sslPort, Arrays.asList(selectedAlias));
208      }
209      else if (rbJCEKS.isSelected())
210      {
211        ops = SecurityOptions.createJCEKSCertificateOptions(
212            tfKeystorePath.getText(),
213            String.valueOf(tfKeystorePwd.getPassword()), enableSSL,
214            enableStartTLS, sslPort, Arrays.asList(selectedAlias));
215      }
216      else if (rbPKCS11.isSelected())
217      {
218        ops = SecurityOptions.createPKCS11CertificateOptions(
219            String.valueOf(tfKeystorePwd.getPassword()), enableSSL,
220            enableStartTLS, sslPort, Arrays.asList(selectedAlias));
221      }
222      else if (rbPKCS12.isSelected())
223      {
224        ops = SecurityOptions.createPKCS12CertificateOptions(
225            tfKeystorePath.getText(),
226            String.valueOf(tfKeystorePwd.getPassword()), enableSSL,
227            enableStartTLS, sslPort, Arrays.asList(selectedAlias));
228      }
229      else
230      {
231        throw new IllegalStateException("No certificate options selected.");
232      }
233    }
234    else
235    {
236      ops = SecurityOptions.createNoCertificateOptions();
237    }
238    return ops;
239  }
240
241  /**
242   * Creates and returns the panel of the dialog.
243   * @return the panel of the dialog.
244   */
245  private JPanel createPanel()
246  {
247    GridBagConstraints gbc = new GridBagConstraints();
248
249    JPanel contentPanel = new JPanel(new GridBagLayout());
250    contentPanel.setBackground(UIFactory.DEFAULT_BACKGROUND);
251    gbc.fill = GridBagConstraints.BOTH;
252    gbc.gridwidth = GridBagConstraints.REMAINDER;
253    gbc.weightx = 1.0;
254
255    JPanel topPanel = new JPanel(new GridBagLayout());
256    topPanel.setBorder(UIFactory.DIALOG_PANEL_BORDER);
257    topPanel.setBackground(UIFactory.CURRENT_STEP_PANEL_BACKGROUND);
258    Insets insets = UIFactory.getCurrentStepPanelInsets();
259
260    gbc.weighty = 0.0;
261    insets.bottom = 0;
262    gbc.insets = insets;
263    topPanel.add(createTitlePanel(), gbc);
264    gbc.insets.top = UIFactory.TOP_INSET_INSTRUCTIONS_SUBPANEL;
265    topPanel.add(createInstructionsPane(), gbc);
266    gbc.insets.top = UIFactory.TOP_INSET_INPUT_SUBPANEL;
267    gbc.insets.bottom = UIFactory.TOP_INSET_INPUT_SUBPANEL;
268    topPanel.add(createInputPanel(), gbc);
269    gbc.weighty = 1.0;
270    gbc.insets = UIFactory.getEmptyInsets();
271    topPanel.add(Box.createVerticalGlue(), gbc);
272    contentPanel.add(topPanel, gbc);
273    gbc.weighty = 0.0;
274    gbc.insets = UIFactory.getButtonsPanelInsets();
275    contentPanel.add(createButtonsPanel(), gbc);
276
277    return contentPanel;
278  }
279
280  /**
281   * Creates and returns the title sub panel.
282   * @return the title sub panel.
283   */
284  private Component createTitlePanel()
285  {
286    JPanel titlePanel = new JPanel(new GridBagLayout());
287    GridBagConstraints gbc = new GridBagConstraints();
288    titlePanel.setOpaque(false);
289    gbc.anchor = GridBagConstraints.NORTHWEST;
290    gbc.fill = GridBagConstraints.BOTH;
291    gbc.weightx = 0.0;
292    gbc.gridwidth = GridBagConstraints.RELATIVE;
293
294    LocalizableMessage title = INFO_SECURITY_OPTIONS_TITLE.get();
295    JLabel l =
296        UIFactory.makeJLabel(UIFactory.IconType.NO_ICON, title,
297            UIFactory.TextStyle.TITLE);
298    l.setOpaque(false);
299    titlePanel.add(l, gbc);
300
301    gbc.gridwidth = GridBagConstraints.RELATIVE;
302    gbc.anchor = GridBagConstraints.NORTHWEST;
303    gbc.weightx = 1.0;
304    gbc.gridwidth = GridBagConstraints.REMAINDER;
305    gbc.insets.left = 0;
306    gbc.weightx = 1.0;
307    gbc.gridwidth = GridBagConstraints.REMAINDER;
308    titlePanel.add(Box.createHorizontalGlue(), gbc);
309
310    return titlePanel;
311  }
312
313  /**
314   * Creates and returns the instructions sub panel.
315   * @return the instructions sub panel.
316   */
317  private Component createInstructionsPane()
318  {
319    LocalizableMessage instructions = INFO_SECURITY_OPTIONS_INSTRUCTIONS.get();
320
321    JTextComponent instructionsPane =
322      UIFactory.makeHtmlPane(instructions, UIFactory.INSTRUCTIONS_FONT);
323    instructionsPane.setOpaque(false);
324    instructionsPane.setEditable(false);
325
326    return instructionsPane;
327  }
328
329  /**
330   * Creates and returns the input sub panel: the panel with all the widgets
331   * that are used to define the security options.
332   * @return the input sub panel.
333   */
334  private Component createInputPanel()
335  {
336    JPanel inputPanel = new JPanel(new GridBagLayout());
337    inputPanel.setOpaque(false);
338
339    ActionListener l = new ActionListener()
340    {
341      @Override
342      public void actionPerformed(ActionEvent ev)
343      {
344        updateEnablingState();
345      }
346    };
347
348    cbEnableSSL = UIFactory.makeJCheckBox(INFO_ENABLE_SSL_LABEL.get(),
349        INFO_ENABLE_SSL_TOOLTIP.get(), UIFactory.TextStyle.PRIMARY_FIELD_VALID);
350    cbEnableSSL.addActionListener(l);
351    String sPort = "";
352    int port = securityOptions.getSslPort();
353    if (port > 0)
354    {
355      sPort = String.valueOf(port);
356    }
357    tfPort = UIFactory.makeJTextField(LocalizableMessage.raw(sPort),
358        INFO_SSL_PORT_TEXTFIELD_TOOLTIP.get(), UIFactory.PORT_FIELD_SIZE,
359        UIFactory.TextStyle.TEXTFIELD);
360    cbEnableStartTLS = UIFactory.makeJCheckBox(INFO_ENABLE_STARTTLS_LABEL.get(),
361        INFO_ENABLE_STARTTLS_TOOLTIP.get(),
362        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
363    cbEnableStartTLS.addActionListener(l);
364    rbUseSelfSignedCertificate = UIFactory.makeJRadioButton(
365        INFO_USE_SELF_SIGNED_LABEL.get(),
366        INFO_USE_SELF_SIGNED_TOOLTIP.get(),
367        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
368    rbUseSelfSignedCertificate.addActionListener(l);
369    rbUseExistingCertificate = UIFactory.makeJRadioButton(
370        INFO_USE_EXISTING_CERTIFICATE_LABEL.get(),
371        INFO_USE_EXISTING_CERTIFICATE_TOOLTIP.get(),
372        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
373    rbUseExistingCertificate.addActionListener(l);
374    ButtonGroup group1 = new ButtonGroup();
375    group1.add(rbUseSelfSignedCertificate);
376    group1.add(rbUseExistingCertificate);
377
378    lKeystoreType = UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
379        INFO_KEYSTORE_TYPE_LABEL.get(),
380        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
381    lKeystoreType.setOpaque(false);
382    rbJKS = UIFactory.makeJRadioButton(
383        INFO_JKS_CERTIFICATE_LABEL.get(),
384        INFO_JKS_CERTIFICATE_TOOLTIP.get(),
385        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
386    rbJKS.addActionListener(l);
387    rbJCEKS = UIFactory.makeJRadioButton(
388        INFO_JCEKS_CERTIFICATE_LABEL.get(),
389        INFO_JCEKS_CERTIFICATE_TOOLTIP.get(),
390        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
391    rbJCEKS.addActionListener(l);
392    rbPKCS11 = UIFactory.makeJRadioButton(
393        INFO_PKCS11_CERTIFICATE_LABEL.get(),
394        INFO_PKCS11_CERTIFICATE_TOOLTIP.get(),
395        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
396    rbPKCS11.addActionListener(l);
397    rbPKCS12 = UIFactory.makeJRadioButton(
398        INFO_PKCS12_CERTIFICATE_LABEL.get(),
399        INFO_PKCS12_CERTIFICATE_TOOLTIP.get(),
400        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
401    rbPKCS12.addActionListener(l);
402    ButtonGroup group2 = new ButtonGroup();
403    group2.add(rbJKS);
404    group2.add(rbJCEKS);
405    group2.add(rbPKCS11);
406    group2.add(rbPKCS12);
407    lKeystoreType.setLabelFor(rbJKS);
408
409    lKeystorePath = UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
410        INFO_KEYSTORE_PATH_LABEL.get(),
411        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
412    lKeystorePath.setOpaque(false);
413    tfKeystorePath = UIFactory.makeJTextField(LocalizableMessage.EMPTY,
414        INFO_KEYSTORE_PATH_TOOLTIP.get(),
415        UIFactory.HOST_FIELD_SIZE, UIFactory.TextStyle.TEXTFIELD);
416    lKeystorePath.setLabelFor(tfKeystorePath);
417    browseButton =
418      UIFactory.makeJButton(INFO_BROWSE_BUTTON_LABEL.get(),
419          INFO_BROWSE_BUTTON_TOOLTIP.get());
420
421    BrowseActionListener browseListener =
422      new BrowseActionListener(tfKeystorePath,
423          BrowseActionListener.BrowseType.GENERIC_FILE,
424          this);
425    browseButton.addActionListener(browseListener);
426
427    lKeystorePwd = UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
428        INFO_KEYSTORE_PWD_LABEL.get(),
429        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
430    lKeystorePwd.setOpaque(false);
431    tfKeystorePwd = UIFactory.makeJPasswordField(LocalizableMessage.EMPTY,
432        INFO_KEYSTORE_PWD_TOOLTIP.get(),
433        UIFactory.PASSWORD_FIELD_SIZE, UIFactory.TextStyle.PASSWORD_FIELD);
434    lKeystorePwd.setLabelFor(tfKeystorePwd);
435
436    GridBagConstraints gbc = new GridBagConstraints();
437    gbc.anchor = GridBagConstraints.WEST;
438    gbc.weightx = 0.0;
439    gbc.gridwidth = GridBagConstraints.RELATIVE;
440    gbc.insets = UIFactory.getEmptyInsets();
441    gbc.fill = GridBagConstraints.HORIZONTAL;
442    inputPanel.add(UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
443        INFO_SSL_ACCESS_LABEL.get(), UIFactory.TextStyle.PRIMARY_FIELD_VALID),
444        gbc);
445
446    JPanel auxPanel = new JPanel(new GridBagLayout());
447    auxPanel.setOpaque(false);
448    gbc.gridwidth = 4;
449    gbc.fill = GridBagConstraints.NONE;
450    auxPanel.add(cbEnableSSL, gbc);
451    gbc.gridwidth--;
452    gbc.insets.left = UIFactory.LEFT_INSET_SECONDARY_FIELD;
453    auxPanel.add(tfPort, gbc);
454    gbc.gridwidth = GridBagConstraints.RELATIVE;
455    auxPanel.add(UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
456        getPortHelpMessage(), UIFactory.TextStyle.SECONDARY_FIELD_VALID), gbc);
457    gbc.gridwidth = GridBagConstraints.REMAINDER;
458    gbc.fill = GridBagConstraints.HORIZONTAL;
459    gbc.weightx = 1.0;
460    auxPanel.add(Box.createHorizontalGlue(), gbc);
461
462    gbc.insets.left = UIFactory.LEFT_INSET_PRIMARY_FIELD;
463    gbc.weightx = 1.0;
464    inputPanel.add(auxPanel, gbc);
465
466    gbc.insets = UIFactory.getEmptyInsets();
467    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
468    gbc.gridwidth = GridBagConstraints.RELATIVE;
469    gbc.weightx = 0.0;
470    inputPanel.add(UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
471        INFO_STARTTLS_ACCESS_LABEL.get(),
472        UIFactory.TextStyle.PRIMARY_FIELD_VALID),
473        gbc);
474    auxPanel = new JPanel(new GridBagLayout());
475    auxPanel.setOpaque(false);
476    gbc.gridwidth = GridBagConstraints.RELATIVE;
477    gbc.insets = UIFactory.getEmptyInsets();
478    auxPanel.add(cbEnableStartTLS, gbc);
479    gbc.weightx = 1.0;
480    gbc.gridwidth = GridBagConstraints.REMAINDER;
481    auxPanel.add(Box.createHorizontalGlue(), gbc);
482    gbc.insets.left = UIFactory.LEFT_INSET_PRIMARY_FIELD;
483    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
484    inputPanel.add(auxPanel, gbc);
485
486    gbc.insets = UIFactory.getEmptyInsets();
487    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
488    gbc.anchor = GridBagConstraints.NORTHWEST;
489    gbc.gridwidth = GridBagConstraints.RELATIVE;
490    gbc.weightx = 0.0;
491    JLabel lCertificate = UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
492        INFO_CERTIFICATE_LABEL.get(), UIFactory.TextStyle.PRIMARY_FIELD_VALID);
493    int additionalInset = Math.abs(lCertificate.getPreferredSize().height -
494        rbUseSelfSignedCertificate.getPreferredSize().height) / 2;
495    gbc.insets.top += additionalInset;
496    inputPanel.add(lCertificate, gbc);
497    auxPanel = new JPanel(new GridBagLayout());
498    auxPanel.setOpaque(false);
499    gbc.insets.left = UIFactory.LEFT_INSET_PRIMARY_FIELD;
500    gbc.gridwidth = GridBagConstraints.REMAINDER;
501    gbc.weightx = 1.0;
502    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
503    inputPanel.add(auxPanel, gbc);
504
505    gbc.insets = UIFactory.getEmptyInsets();
506    gbc.anchor = GridBagConstraints.WEST;
507    JPanel aux2Panel = new JPanel(new GridBagLayout());
508    aux2Panel.setOpaque(false);
509    gbc.gridwidth = GridBagConstraints.RELATIVE;
510    aux2Panel.add(rbUseSelfSignedCertificate, gbc);
511    gbc.weightx = 1.0;
512    gbc.gridwidth = GridBagConstraints.REMAINDER;
513    aux2Panel.add(Box.createHorizontalGlue(), gbc);
514    auxPanel.add(aux2Panel, gbc);
515
516    aux2Panel = new JPanel(new GridBagLayout());
517    aux2Panel.setOpaque(false);
518    gbc.gridwidth = GridBagConstraints.RELATIVE;
519    gbc.insets = UIFactory.getEmptyInsets();
520    gbc.weightx = 0.0;
521    aux2Panel.add(rbUseExistingCertificate, gbc);
522    gbc.weightx = 1.0;
523    gbc.gridwidth = GridBagConstraints.REMAINDER;
524    aux2Panel.add(Box.createHorizontalGlue(), gbc);
525    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
526    auxPanel.add(aux2Panel, gbc);
527
528    additionalInset = Math.abs(lKeystoreType.getPreferredSize().height -
529        rbJKS.getPreferredSize().height) / 2;
530    aux2Panel = new JPanel(new GridBagLayout());
531    aux2Panel.setOpaque(false);
532    gbc.insets.top -= additionalInset;
533    gbc.insets.left = UIFactory.LEFT_INSET_RADIO_SUBORDINATE;
534    auxPanel.add(aux2Panel, gbc);
535
536    gbc.gridwidth = GridBagConstraints.RELATIVE;
537    gbc.insets = UIFactory.getEmptyInsets();
538    gbc.weightx = 0.0;
539    gbc.anchor = GridBagConstraints.NORTHWEST;
540    gbc.insets.top = additionalInset;
541    aux2Panel.add(lKeystoreType, gbc);
542    gbc.gridwidth = GridBagConstraints.REMAINDER;
543    gbc.insets.top = 0;
544    aux2Panel.add(rbJKS, gbc);
545
546    gbc.insets.top = UIFactory.TOP_INSET_RADIOBUTTON;
547    gbc.gridwidth = GridBagConstraints.RELATIVE;
548    aux2Panel.add(Box.createHorizontalGlue(), gbc);
549    gbc.gridwidth = GridBagConstraints.REMAINDER;
550    aux2Panel.add(rbJCEKS, gbc);
551    gbc.gridwidth = GridBagConstraints.RELATIVE;
552    aux2Panel.add(Box.createHorizontalGlue(), gbc);
553    gbc.gridwidth = GridBagConstraints.REMAINDER;
554    aux2Panel.add(rbPKCS12, gbc);
555    gbc.gridwidth = GridBagConstraints.RELATIVE;
556    aux2Panel.add(Box.createHorizontalGlue(), gbc);
557    gbc.gridwidth = GridBagConstraints.REMAINDER;
558    aux2Panel.add(rbPKCS11, gbc);
559
560    gbc.gridwidth = GridBagConstraints.RELATIVE;
561    gbc.insets.left = 0;
562    gbc.weightx = 0.0;
563    gbc.anchor = GridBagConstraints.WEST;
564    aux2Panel.add(lKeystorePath, gbc);
565    JPanel aux3Panel = new JPanel(new GridBagLayout());
566    aux3Panel.setOpaque(false);
567    gbc.weightx = 1.0;
568    gbc.insets.left = UIFactory.LEFT_INSET_SECONDARY_FIELD;
569    gbc.gridwidth = GridBagConstraints.REMAINDER;
570    aux2Panel.add(aux3Panel, gbc);
571    gbc.insets = UIFactory.getEmptyInsets();
572    gbc.gridwidth = GridBagConstraints.RELATIVE;
573    aux3Panel.add(tfKeystorePath, gbc);
574    gbc.insets.left = UIFactory.LEFT_INSET_BROWSE;
575    gbc.gridwidth = GridBagConstraints.REMAINDER;
576    gbc.weightx = 0.0;
577    aux3Panel.add(browseButton, gbc);
578
579    gbc.insets.left = 0;
580    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
581    gbc.gridwidth = GridBagConstraints.RELATIVE;
582    gbc.weightx = 0.0;
583    gbc.anchor = GridBagConstraints.WEST;
584    aux2Panel.add(lKeystorePwd, gbc);
585    gbc.insets.left = UIFactory.LEFT_INSET_SECONDARY_FIELD;
586    gbc.gridwidth = GridBagConstraints.REMAINDER;
587    gbc.fill = GridBagConstraints.NONE;
588    aux2Panel.add(tfKeystorePwd, gbc);
589
590    return inputPanel;
591  }
592
593  /**
594   * Creates and returns the buttons OK/CANCEL sub panel.
595   * @return the buttons OK/CANCEL sub panel.
596   */
597  private Component createButtonsPanel()
598  {
599    JPanel buttonsPanel = new JPanel(new GridBagLayout());
600    buttonsPanel.setOpaque(false);
601    GridBagConstraints gbc = new GridBagConstraints();
602    gbc.fill = GridBagConstraints.HORIZONTAL;
603    gbc.gridwidth = 4;
604    gbc.insets = UIFactory.getEmptyInsets();
605    gbc.insets.left = UIFactory.getCurrentStepPanelInsets().left;
606    buttonsPanel.add(UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
607        null, UIFactory.TextStyle.NO_STYLE), gbc);
608    gbc.weightx = 1.0;
609    gbc.gridwidth--;
610    gbc.insets.left = 0;
611    buttonsPanel.add(Box.createHorizontalGlue(), gbc);
612    gbc.gridwidth = GridBagConstraints.RELATIVE;
613    gbc.fill = GridBagConstraints.NONE;
614    gbc.weightx = 0.0;
615    okButton =
616      UIFactory.makeJButton(INFO_OK_BUTTON_LABEL.get(),
617          INFO_SECURITY_OPTIONS_OK_BUTTON_TOOLTIP.get());
618    buttonsPanel.add(okButton, gbc);
619    okButton.addActionListener(new ActionListener()
620    {
621      @Override
622      public void actionPerformed(ActionEvent ev)
623      {
624        okClicked();
625      }
626    });
627
628    gbc.gridwidth = GridBagConstraints.REMAINDER;
629    gbc.insets.left = UIFactory.HORIZONTAL_INSET_BETWEEN_BUTTONS;
630    cancelButton =
631      UIFactory.makeJButton(INFO_CANCEL_BUTTON_LABEL.get(),
632          INFO_SECURITY_OPTIONS_CANCEL_BUTTON_TOOLTIP.get());
633    buttonsPanel.add(cancelButton, gbc);
634    cancelButton.addActionListener(new ActionListener()
635    {
636      @Override
637      public void actionPerformed(ActionEvent ev)
638      {
639        cancelClicked();
640      }
641    });
642
643    return buttonsPanel;
644  }
645
646  /** Method called when user clicks on cancel. */
647  private void cancelClicked()
648  {
649    isCanceled = true;
650    dispose();
651  }
652
653  /** Method called when user clicks on OK. */
654  private void okClicked()
655  {
656    BackgroundTask<ArrayList<LocalizableMessage>> worker =
657      new BackgroundTask<ArrayList<LocalizableMessage>>()
658    {
659      @Override
660      public ArrayList<LocalizableMessage> processBackgroundTask()
661      {
662        ArrayList<LocalizableMessage> errorMsgs = new ArrayList<>();
663        errorMsgs.addAll(checkPort());
664        errorMsgs.addAll(checkKeystore());
665        return errorMsgs;
666      }
667
668      @Override
669      public void backgroundTaskCompleted(ArrayList<LocalizableMessage> returnValue,
670          Throwable throwable)
671      {
672        if (throwable != null)
673        {
674          // Bug
675          throwable.printStackTrace();
676          displayError(
677              getThrowableMsg(INFO_BUG_MSG.get(), throwable),
678              INFO_ERROR_TITLE.get());
679          cancelButton.setEnabled(true);
680          okButton.setEnabled(true);
681        }
682        else
683        {
684          cancelButton.setEnabled(true);
685          okButton.setEnabled(true);
686
687          if (!returnValue.isEmpty())
688          {
689            displayError(Utils.getMessageFromCollection(returnValue, "\n"),
690                INFO_ERROR_TITLE.get());
691          }
692          else if (rbUseExistingCertificate.isSelected()
693              && (cbEnableSSL.isSelected() || cbEnableStartTLS.isSelected()))
694          {
695            if (!certificateHasAlias)
696            {
697              selectedAlias = null;
698              isCanceled = false;
699              dispose();
700            }
701            else if (aliases.length > 1)
702            {
703              if (aliasDlg == null)
704              {
705                aliasDlg = new SelectAliasDialog(SecurityOptionsDialog.this);
706              }
707              aliasDlg.display(aliases);
708
709              if (!aliasDlg.isCanceled())
710              {
711                selectedAlias = aliasDlg.getSelectedAlias();
712                isCanceled = false;
713                dispose();
714              }
715            }
716            else
717            {
718              selectedAlias = aliases[0];
719              isCanceled = false;
720              dispose();
721            }
722          }
723          else
724          {
725            isCanceled = false;
726            dispose();
727          }
728        }
729      }
730    };
731    cancelButton.setEnabled(false);
732    okButton.setEnabled(false);
733    worker.startBackgroundTask();
734  }
735
736  /**
737   * Displays an error message dialog.
738   *
739   * @param msg
740   *          the error message.
741   * @param title
742   *          the title for the dialog.
743   */
744  private void displayError(LocalizableMessage msg, LocalizableMessage title)
745  {
746    Utilities.displayError(this, msg, title);
747    toFront();
748  }
749
750  /** Updates the widgets on the dialog with the contents of the securityOptions object. */
751  private void updateContents()
752  {
753    cbEnableSSL.setSelected(securityOptions.getEnableSSL());
754    cbEnableStartTLS.setSelected(securityOptions.getEnableStartTLS());
755    if (securityOptions.getEnableSSL())
756    {
757      int port = securityOptions.getSslPort();
758      if (port > 0)
759      {
760        tfPort.setText(String.valueOf(port));
761      }
762    }
763
764    switch (securityOptions.getCertificateType())
765    {
766    case NO_CERTIFICATE:
767      // Nothing else to do
768      break;
769
770    case SELF_SIGNED_CERTIFICATE:
771      rbUseSelfSignedCertificate.setSelected(true);
772      break;
773
774    case JKS:
775      rbUseExistingCertificate.setSelected(true);
776      rbJKS.setSelected(true);
777      tfKeystorePath.setText(securityOptions.getKeystorePath());
778      tfKeystorePwd.setText(securityOptions.getKeystorePassword());
779      break;
780
781    case JCEKS:
782      rbUseExistingCertificate.setSelected(true);
783      rbJCEKS.setSelected(true);
784      tfKeystorePath.setText(securityOptions.getKeystorePath());
785      tfKeystorePwd.setText(securityOptions.getKeystorePassword());
786      break;
787
788    case PKCS11:
789      rbUseExistingCertificate.setSelected(true);
790      rbPKCS11.setSelected(true);
791      tfKeystorePwd.setText(securityOptions.getKeystorePassword());
792      break;
793
794    case PKCS12:
795      rbUseExistingCertificate.setSelected(true);
796      rbPKCS12.setSelected(true);
797      tfKeystorePath.setText(securityOptions.getKeystorePath());
798      tfKeystorePwd.setText(securityOptions.getKeystorePassword());
799      break;
800
801    default:
802      throw new IllegalStateException("Unknown certificate type.");
803    }
804
805    updateEnablingState();
806  }
807
808  /**
809   * Enables/disables and makes visible/invisible the objects according to what
810   * the user selected.
811   */
812  private void updateEnablingState()
813  {
814    boolean enableSSL = cbEnableSSL.isSelected();
815    boolean enableStartTLS = cbEnableStartTLS.isSelected();
816
817    boolean useSSL = enableSSL || enableStartTLS;
818
819    if (useSSL && !rbUseSelfSignedCertificate.isSelected() &&
820        !rbUseExistingCertificate.isSelected())
821    {
822      rbUseSelfSignedCertificate.setSelected(true);
823    }
824
825    if (useSSL && rbUseExistingCertificate.isSelected() &&
826        !rbJKS.isSelected() && !rbJCEKS.isSelected() &&
827        !rbPKCS11.isSelected() && !rbPKCS12.isSelected())
828    {
829      rbJKS.setSelected(true);
830    }
831    tfPort.setEnabled(enableSSL);
832
833    rbUseSelfSignedCertificate.setEnabled(useSSL);
834
835    rbUseExistingCertificate.setEnabled(useSSL);
836    lKeystoreType.setEnabled(
837        rbUseExistingCertificate.isSelected() && useSSL);
838    rbJKS.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
839    rbJCEKS.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
840    rbPKCS11.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
841    rbPKCS12.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
842
843    lKeystorePath.setEnabled(
844        rbUseExistingCertificate.isSelected() && useSSL);
845    tfKeystorePath.setEnabled(
846        rbUseExistingCertificate.isSelected() && useSSL);
847    browseButton.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
848    lKeystorePwd.setEnabled(
849        rbUseExistingCertificate.isSelected() && useSSL);
850    tfKeystorePwd.setEnabled(
851        rbUseExistingCertificate.isSelected() && useSSL);
852
853    lKeystorePath.setVisible(!rbPKCS11.isSelected());
854    tfKeystorePath.setVisible(!rbPKCS11.isSelected());
855    browseButton.setVisible(!rbPKCS11.isSelected());
856  }
857
858  /**
859   * Returns the port help message that we display when we cannot use the
860   * default port (636).
861   * @return the port help message that we display when we cannot use the
862   * default port (636).
863   */
864  private LocalizableMessage getPortHelpMessage()
865  {
866    LocalizableMessage s = LocalizableMessage.EMPTY;
867    if (securityOptions.getSslPort() != DEFAULT_PORT)
868    {
869      s = INFO_CANNOT_USE_DEFAULT_SECURE_PORT.get();
870    }
871    return s;
872  }
873
874  /**
875   * Checks the port.
876   * @return the error messages found while checking the port.
877   */
878  private ArrayList<LocalizableMessage> checkPort()
879  {
880    ArrayList<LocalizableMessage> errorMsgs = new ArrayList<>();
881
882    if (cbEnableSSL.isSelected())
883    {
884      /* Check the port. */
885      String sPort = tfPort.getText();
886      int port = -1;
887      try
888      {
889        port = Integer.parseInt(sPort);
890        if (port < Installer.MIN_PORT_VALUE
891            || port > Installer.MAX_PORT_VALUE)
892        {
893          errorMsgs.add(INFO_INVALID_SECURE_PORT_VALUE_RANGE.get(
894              Installer.MIN_PORT_VALUE, Installer.MAX_PORT_VALUE));
895        }
896        else if (!Utils.canUseAsPort(port))
897        {
898          if (Utils.isPrivilegedPort(port))
899          {
900            errorMsgs.add(INFO_CANNOT_BIND_PRIVILEDGED_PORT.get(port));
901          }
902          else
903          {
904            errorMsgs.add(INFO_CANNOT_BIND_PORT.get(port));
905          }
906        }
907      }
908      catch (NumberFormatException nfe)
909      {
910        errorMsgs.add(INFO_INVALID_SECURE_PORT_VALUE_RANGE.get(
911            Installer.MIN_PORT_VALUE, Installer.MAX_PORT_VALUE));
912      }
913    }
914    setValidLater(cbEnableSSL, errorMsgs.isEmpty());
915    return errorMsgs;
916  }
917
918  /**
919   * Checks the existing keystore parameters.
920   * @return the error messages found while checking existing keystore
921   * parameters.
922   */
923  private ArrayList<LocalizableMessage> checkKeystore()
924  {
925    ArrayList<LocalizableMessage> errorMsgs = new ArrayList<>();
926
927    boolean pathValid = true;
928    boolean pwdValid = true;
929
930    if (rbUseExistingCertificate.isSelected() &&
931        (cbEnableSSL.isSelected() || cbEnableStartTLS.isSelected()))
932    {
933      String path = tfKeystorePath.getText();
934      if (rbJKS.isSelected() || rbJCEKS.isSelected() || rbPKCS12.isSelected())
935      {
936        /* Check the path */
937        if (path == null || path.length() == 0)
938        {
939          errorMsgs.add(INFO_KEYSTORE_PATH_NOT_PROVIDED.get());
940        }
941        else
942        {
943          File f = new File(path);
944          if (!f.exists())
945          {
946            errorMsgs.add(INFO_KEYSTORE_PATH_DOES_NOT_EXIST.get());
947          }
948          else if (!f.isFile())
949          {
950            errorMsgs.add(INFO_KEYSTORE_PATH_NOT_A_FILE.get());
951          }
952        }
953
954        pathValid = errorMsgs.isEmpty();
955      }
956
957      String pwd = String.valueOf(tfKeystorePwd.getPassword());
958      if (pathValid)
959      {
960        try
961        {
962          CertificateManager certManager;
963          if (rbJKS.isSelected())
964          {
965            certManager = new CertificateManager(
966                path,
967                CertificateManager.KEY_STORE_TYPE_JKS,
968                pwd);
969          }
970          else if (rbJCEKS.isSelected())
971          {
972            certManager = new CertificateManager(
973                path,
974                CertificateManager.KEY_STORE_TYPE_JCEKS,
975                pwd);
976          }
977          else if (rbPKCS12.isSelected())
978          {
979            certManager = new CertificateManager(
980                path,
981                CertificateManager.KEY_STORE_TYPE_PKCS12,
982                pwd);
983          }
984          else if (rbPKCS11.isSelected())
985          {
986            certManager = new CertificateManager(
987                CertificateManager.KEY_STORE_PATH_PKCS11,
988                CertificateManager.KEY_STORE_TYPE_PKCS11,
989                pwd);
990          }
991          else
992          {
993            throw new IllegalStateException("No keystore type selected.");
994          }
995          aliases = certManager.getCertificateAliases();
996          if (aliases == null || aliases.length == 0)
997          {
998            // Could not retrieve any certificate
999            if (rbPKCS11.isSelected())
1000            {
1001              errorMsgs.add(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST.get());
1002            }
1003            else
1004            {
1005              if (rbJKS.isSelected())
1006              {
1007                errorMsgs.add(INFO_JKS_KEYSTORE_DOES_NOT_EXIST.get());
1008              }
1009              else if (rbJCEKS.isSelected())
1010              {
1011                errorMsgs.add(INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST.get());
1012              }
1013              else
1014              {
1015                errorMsgs.add(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST.get());
1016              }
1017              pathValid = false;
1018            }
1019          }
1020          else
1021          {
1022            certificateHasAlias = certManager.hasRealAliases();
1023          }
1024        }
1025        catch (KeyStoreException ke)
1026        {
1027          // issue OPENDJ-18, related to JDK bug
1028          if (StaticUtils
1029              .stackTraceContainsCause(ke, ArithmeticException.class))
1030          {
1031            errorMsgs.add(INFO_ERROR_ACCESSING_KEYSTORE_JDK_BUG.get());
1032          }
1033          else
1034          {
1035            pwdValid = false;
1036            if (!rbPKCS11.isSelected())
1037            {
1038              pathValid = false;
1039            }
1040            // Could not access to the keystore: because the password is
1041            // no good, because the provided file is not a valid keystore, etc.
1042            if (rbPKCS11.isSelected())
1043            {
1044              errorMsgs.add(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE.get());
1045            }
1046            else
1047            {
1048              if (rbJKS.isSelected())
1049              {
1050                errorMsgs.add(INFO_ERROR_ACCESSING_JKS_KEYSTORE.get());
1051              }
1052              else if (rbJCEKS.isSelected())
1053              {
1054                errorMsgs.add(INFO_ERROR_ACCESSING_JCEKS_KEYSTORE.get());
1055              }
1056              else
1057              {
1058                errorMsgs.add(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE.get());
1059              }
1060              pathValid = false;
1061            }
1062          }
1063        }
1064      }
1065    }
1066
1067    setValidLater(lKeystorePath, pathValid);
1068    setValidLater(lKeystorePwd, pwdValid);
1069
1070    return errorMsgs;
1071  }
1072
1073  /**
1074   * Method that updates the text style of a provided component by calling
1075   * SwingUtilities.invokeLater.  This method is aimed to be called outside
1076   * the event thread (calling it from the event thread will also work though).
1077   * @param comp the component to be updated.
1078   * @param valid whether to use a TextStyle to mark the component as valid
1079   * or as invalid.
1080   */
1081  private void setValidLater(final JComponent comp, final boolean valid)
1082  {
1083    SwingUtilities.invokeLater(new Runnable()
1084    {
1085      @Override
1086      public void run()
1087      {
1088        UIFactory.setTextStyle(comp,
1089            valid ? UIFactory.TextStyle.SECONDARY_FIELD_VALID :
1090              UIFactory.TextStyle.SECONDARY_FIELD_INVALID);
1091      }
1092    });
1093  }
1094
1095  /**
1096   * Method written for testing purposes.
1097   * @param args the arguments to be passed to the test program.
1098   */
1099  public static void main(String[] args)
1100  {
1101    try
1102    {
1103      // UIFactory.initialize();
1104      SecurityOptionsDialog dlg = new SecurityOptionsDialog(new JFrame(),
1105          SecurityOptions.createNoCertificateOptions());
1106      dlg.pack();
1107      dlg.setVisible(true);
1108    } catch (Exception ex)
1109    {
1110      ex.printStackTrace();
1111    }
1112  }
1113}