/*
* Copyright 2016 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.thoughtworks.go.agent.bootstrapper.osx;
import com.beust.jcommander.ParameterException;
import com.thoughtworks.go.agent.common.AgentBootstrapperArgs;
import com.thoughtworks.go.agent.common.CertificateFileValidator;
import com.thoughtworks.go.agent.common.ServerUrlValidator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
public class MacPreferencesPane extends JFrame {
private static final Log LOG = LogFactory.getLog(AgentMacWindow.class);
private JTextField serverTextField;
private FileBrowser fileBrowser;
private JButton okButton;
private AgentMacWindow agentMacWindow;
private SslModeComponent sslModeComponent;
public MacPreferencesPane(final AgentMacWindow agentMacWindow) {
super();
this.agentMacWindow = agentMacWindow;
BorderLayout border = new BorderLayout(10, 10);
getContentPane().setLayout(border);
createView();
sslModeComponent.noneModeRadioButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileBrowser.setEnabled(!sslModeComponent.noneModeRadioButton.isSelected());
}
});
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent newEvent) {
try {
AgentBootstrapperArgs newArgs = new AgentBootstrapperArgs(
new URL(serverTextField.getText()),
fileBrowser.getFile(), sslModeComponent.getSslMode());
try {
new ServerUrlValidator().validate("The server url", newArgs.getServerUrl().toExternalForm());
} catch (ParameterException e) {
JOptionPane.showMessageDialog(getContentPane(), e.getMessage(), "Invalid server url", JOptionPane.ERROR_MESSAGE);
return;
}
if (newArgs.getRootCertFile() != null) {
try {
new CertificateFileValidator().validate("The server root certificate", newArgs.getRootCertFile().getPath());
} catch (ParameterException e) {
JOptionPane.showMessageDialog(getContentPane(), e.getMessage(), "Invalid server root certificate", JOptionPane.ERROR_MESSAGE);
return;
}
}
if (!newArgs.equals(agentMacWindow.getBootstrapperArgs())) {
agentMacWindow.setBootstrapperArgs(newArgs);
LOG.info("Updating preferences to " + newArgs);
} else {
LOG.info("Preferences are unchanged " + newArgs);
}
setVisible(false);
} catch (MalformedURLException e) {
JOptionPane.showMessageDialog(getContentPane(), "The server url must be an HTTPS url and must begin with https://", "Invalid server url", JOptionPane.ERROR_MESSAGE);
}
}
});
setSize(getPreferredSize());
setLocation(20, 30);
setResizable(false);
}
private void createView() {
JPanel controlsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10));
serverTextField = new JTextField("");
serverTextField.setColumns(15);
serverTextField.selectAll();
JPanel textPanel = new JPanel(new GridLayout(4, 2, 0, 10));
textPanel.add(new JLabel("Go Server Hostname or IP"));
textPanel.add(serverTextField);
textPanel.add(new JLabel("SSL Mode"));
sslModeComponent = new SslModeComponent();
textPanel.add(sslModeComponent);
textPanel.add(new JLabel("Server root certificate"));
fileBrowser = new FileBrowser();
textPanel.add(fileBrowser);
controlsPanel.add(textPanel);
getContentPane().add(controlsPanel, BorderLayout.NORTH);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10));
okButton = new JButton("OK");
buttonPanel.add(okButton);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
}
public void ask() {
AgentBootstrapperArgs bootstrapperArgs = agentMacWindow.getBootstrapperArgs();
this.serverTextField.setText(bootstrapperArgs.getServerUrl().toString());
this.fileBrowser.setFile(bootstrapperArgs.getRootCertFile());
this.sslModeComponent.setSslMode(bootstrapperArgs.getSslMode());
serverTextField.selectAll();
serverTextField.grabFocus();
setVisible(true);
}
private class FileBrowser extends JPanel {
private File file;
private final JTextField textField;
private final JButton browse;
FileBrowser() {
super();
setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
textField = new JTextField(15);
textField.setEnabled(false);
add(textField);
browse = new JButton("Browse");
add(browse);
browse.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser jFileChooser = new JFileChooser(file != null ? file.getParentFile() : null);
int returnVal = jFileChooser.showOpenDialog(FileBrowser.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
setFile(jFileChooser.getSelectedFile());
}
}
});
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
browse.setEnabled(enabled);
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
if (file != null) {
textField.setText(file.toString());
}
}
}
private class SslModeComponent extends JPanel {
private AgentBootstrapperArgs.SslMode sslVerificationMode;
private final JRadioButton fullModeRadioButton;
private final JRadioButton noneModeRadioButton;
private final JRadioButton noHostVerifyModeRadioButton;
SslModeComponent() {
super();
setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
fullModeRadioButton = new JRadioButton("Full Verification");
fullModeRadioButton.putClientProperty("SSL_MODE", AgentBootstrapperArgs.SslMode.FULL);
fullModeRadioButton.setToolTipText("Perform a complete SSL verification before connecting to the agent.");
noneModeRadioButton = new JRadioButton("No verification");
noneModeRadioButton.putClientProperty("SSL_MODE", AgentBootstrapperArgs.SslMode.NONE);
noneModeRadioButton.setToolTipText("Completely disable any SSL verification");
noHostVerifyModeRadioButton = new JRadioButton("Don't verify host");
noHostVerifyModeRadioButton.putClientProperty("SSL_MODE", AgentBootstrapperArgs.SslMode.NO_VERIFY_HOST);
noHostVerifyModeRadioButton.setToolTipText("Verify the server certificate, but not the hostname.");
ButtonGroup sslModeButtonGroup = new ButtonGroup();
ActionListener actionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JRadioButton b = (JRadioButton) e.getSource();
setSslMode((AgentBootstrapperArgs.SslMode) b.getClientProperty("SSL_MODE"));
}
};
for (JRadioButton button : Arrays.asList(fullModeRadioButton, noneModeRadioButton, noHostVerifyModeRadioButton)) {
sslModeButtonGroup.add(button);
add(button);
button.addActionListener(actionListener);
}
}
private void setSslMode(AgentBootstrapperArgs.SslMode sslVerificationMode) {
switch (sslVerificationMode) {
case FULL:
fullModeRadioButton.setSelected(true);
fileBrowser.setEnabled(true);
break;
case NONE:
noneModeRadioButton.setSelected(true);
fileBrowser.setEnabled(false);
break;
case NO_VERIFY_HOST:
noHostVerifyModeRadioButton.setSelected(true);
fileBrowser.setEnabled(true);
break;
}
this.sslVerificationMode = sslVerificationMode;
}
public AgentBootstrapperArgs.SslMode getSslMode() {
return sslVerificationMode;
}
}
}