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