/*********************************************************************************** * * Copyright (c) 2015 Kamil Baczkowicz * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Kamil Baczkowicz - initial API and implementation and/or initial documentation * */ package pl.baczkowicz.mqttspy.ui.controllers.edit; import java.net.URL; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.PasswordField; import javafx.scene.control.RadioButton; import javafx.scene.control.Tab; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javafx.util.Callback; import javafx.util.StringConverter; import javax.net.ssl.SSLContext; import pl.baczkowicz.mqttspy.common.generated.SecureSocketSettings; import pl.baczkowicz.spy.common.generated.UserCredentials; import pl.baczkowicz.mqttspy.configuration.ConfiguredConnectionDetails; import pl.baczkowicz.mqttspy.configuration.generated.UserAuthenticationOptions; import pl.baczkowicz.mqttspy.configuration.generated.UserInterfaceMqttConnectionDetails; import pl.baczkowicz.mqttspy.ui.ConnectionController; import pl.baczkowicz.mqttspy.ui.EditConnectionController; import pl.baczkowicz.spy.common.generated.SecureSocketModeEnum; import pl.baczkowicz.spy.configuration.BaseConfigurationUtils; /** * Controller for editing a single connection - security tab. */ @SuppressWarnings({"unchecked", "rawtypes"}) public class EditConnectionSecurityController extends AnchorPane implements Initializable, EditConnectionSubController { /** The parent controller. */ private EditConnectionController parent; /** * The name of this field needs to be set to the name of the pane + * Controller (i.e. <fx:id>Controller). */ @FXML private EditConnectionSecurityTlsCertificatesController certificatesPaneController; /** * The name of this field needs to be set to the name of the pane + * Controller (i.e. <fx:id>Controller). */ @FXML private EditConnectionSecurityTlsKeyStoresController keyStoresPaneController; /** * The name of this field needs to be set to the name of the pane + * Controller (i.e. <fx:id>Controller). */ @FXML private EditConnectionSecurityTlsPropertiesController propertiesPaneController; // Security @FXML private CheckBox userAuthentication; @FXML private TextField username; @FXML private RadioButton predefinedUsername; @FXML private RadioButton askForUsername; @FXML private RadioButton askForPassword; @FXML private RadioButton predefinedPassword; @FXML private PasswordField password; @FXML private ComboBox<SecureSocketModeEnum> modeCombo; @FXML private ComboBox<String> protocolCombo; @FXML private AnchorPane customSocketFactoryPane; @FXML private Tab tlsTab; @FXML private Tab authTab; // Other fields private final ChangeListener basicOnChangeListener = new ChangeListener() { @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) { onChange(); } }; // =============================== // === Initialisation ============ // =============================== public void initialize(URL location, ResourceBundle resources) { // Authentication userAuthentication.selectedProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) { updateUserAuthentication(); onChange(); } }); username.textProperty().addListener(basicOnChangeListener); password.textProperty().addListener(basicOnChangeListener); askForUsername.selectedProperty().addListener(basicOnChangeListener); askForPassword.selectedProperty().addListener(basicOnChangeListener); predefinedUsername.selectedProperty().addListener(basicOnChangeListener); predefinedPassword.selectedProperty().addListener(basicOnChangeListener); // SSL protocolCombo.getSelectionModel().selectedIndexProperty().addListener(basicOnChangeListener); final Map<SecureSocketModeEnum, String> modeEnumText = new HashMap<>(); modeEnumText.put(SecureSocketModeEnum.DISABLED, "Disabled"); // Certificates and keys provided externally, e.g. // -Djavax.net.ssl.trustStore=/home/kamil/certificates/public_brokers.jks // -Djavax.net.ssl.trustStorePassword=password modeEnumText.put(SecureSocketModeEnum.BASIC, "Certificates & keys provided externally"); // Server only - cert / trust store modeEnumText.put(SecureSocketModeEnum.SERVER_ONLY, "CA certificate"); modeEnumText.put(SecureSocketModeEnum.SERVER_KEYSTORE, "CA trust store"); // Server and client modeEnumText.put(SecureSocketModeEnum.SERVER_AND_CLIENT, "CA certificate & client certificate/key"); modeEnumText.put(SecureSocketModeEnum.SERVER_AND_CLIENT_KEYSTORES, "CA trust store & client key store"); // SSL&TLS properties modeEnumText.put(SecureSocketModeEnum.PROPERTIES, "TLS/SSL properties"); try { final SSLContext context = SSLContext.getDefault(); final String[] values = context.getSupportedSSLParameters().getProtocols(); final List<String> filteredValues = new ArrayList<>(); filteredValues.addAll(Arrays.asList(values)); final Iterator<String> i = filteredValues.iterator(); while (i.hasNext()) { if (i.next().contains("Hello")) { i.remove(); } } protocolCombo.getSelectionModel().selectedIndexProperty().addListener(basicOnChangeListener); protocolCombo.getItems().addAll(filteredValues); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } // SSL Mode modeCombo.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) { updateSSL(); onChange(); } }); modeCombo.setCellFactory(new Callback<ListView<SecureSocketModeEnum>, ListCell<SecureSocketModeEnum>>() { @Override public ListCell<SecureSocketModeEnum> call(ListView<SecureSocketModeEnum> l) { return new ListCell<SecureSocketModeEnum>() { @Override protected void updateItem(SecureSocketModeEnum item, boolean empty) { super.updateItem(item, empty); if (item == null || empty) { setText(null); } else { setText(modeEnumText.get(item)); } } }; } }); modeCombo.setConverter(new StringConverter<SecureSocketModeEnum>() { @Override public String toString(SecureSocketModeEnum item) { if (item == null) { return null; } else { return modeEnumText.get(item); } } @Override public SecureSocketModeEnum fromString(String id) { return null; } }); for (SecureSocketModeEnum modeEnum : SecureSocketModeEnum.values()) { modeCombo.getItems().add(modeEnum); } } public void init() { certificatesPaneController.setParent(parent); keyStoresPaneController.setParent(parent); propertiesPaneController.setParent(parent); } // =============================== // === Logic ===================== // =============================== public void onChange() { parent.onChange(); } public void updateSSL() { final SecureSocketModeEnum mode = modeCombo.getSelectionModel().getSelectedItem(); final boolean certificates = SecureSocketModeEnum.SERVER_ONLY.equals(mode) || SecureSocketModeEnum.SERVER_AND_CLIENT.equals(mode); final boolean keyStores = SecureSocketModeEnum.SERVER_KEYSTORE.equals(mode) || SecureSocketModeEnum.SERVER_AND_CLIENT_KEYSTORES.equals(mode); // Set up pane visibility propertiesPaneController.getPropertiesPane().setVisible(SecureSocketModeEnum.PROPERTIES.equals(mode)); certificatesPaneController.getTlsCertificatesPane().setVisible(certificates); keyStoresPaneController.getTlsKeyStoresPane().setVisible(keyStores); customSocketFactoryPane.setVisible(certificates || keyStores || SecureSocketModeEnum.BASIC.equals(mode)); if (certificates) { certificatesPaneController.updateSSL(mode); } else if (keyStores) { keyStoresPaneController.updateSSL(mode); } updateTlsIcon(!SecureSocketModeEnum.DISABLED.equals(mode)); } @Override public UserInterfaceMqttConnectionDetails readValues(final UserInterfaceMqttConnectionDetails connection) { if (userAuthentication.isSelected()) { final UserAuthenticationOptions userAuthentication = new UserAuthenticationOptions(); userAuthentication.setAskForUsername(askForUsername.isSelected()); userAuthentication.setAskForPassword(askForPassword.isSelected()); final UserCredentials userCredentials = new UserCredentials(); userCredentials.setUsername(username.getText()); userCredentials.setPassword(BaseConfigurationUtils.encodePassword(password.getText())); connection.setUserAuthentication(userAuthentication); connection.setUserCredentials(userCredentials); } if (modeCombo.getSelectionModel().getSelectedItem() == null || SecureSocketModeEnum.DISABLED.equals(modeCombo.getSelectionModel().getSelectedItem())) { connection.setSSL(null); } else { final SecureSocketSettings sslSettings = new SecureSocketSettings(); sslSettings.setMode(modeCombo.getSelectionModel().getSelectedItem()); connection.setSSL(sslSettings); final boolean certificates = SecureSocketModeEnum.SERVER_ONLY.equals(modeCombo.getSelectionModel().getSelectedItem()) || SecureSocketModeEnum.SERVER_AND_CLIENT.equals(modeCombo.getSelectionModel().getSelectedItem()); final boolean keyStores = SecureSocketModeEnum.SERVER_KEYSTORE.equals(modeCombo.getSelectionModel().getSelectedItem()) || SecureSocketModeEnum.SERVER_AND_CLIENT_KEYSTORES.equals(modeCombo.getSelectionModel().getSelectedItem()); propertiesPaneController.readAndSetValues(modeCombo.getSelectionModel().getSelectedItem(), connection); if (SecureSocketModeEnum.BASIC.equals(modeCombo.getSelectionModel().getSelectedItem())) { sslSettings.setProtocol(protocolCombo.getSelectionModel().getSelectedItem()); } else if (certificates) { sslSettings.setProtocol(protocolCombo.getSelectionModel().getSelectedItem()); certificatesPaneController.readAndSetValues(modeCombo.getSelectionModel().getSelectedItem(), connection); } else if (keyStores) { sslSettings.setProtocol(protocolCombo.getSelectionModel().getSelectedItem()); keyStoresPaneController.readAndSetValues(modeCombo.getSelectionModel().getSelectedItem(), connection); } } return connection; } public void updateUserAuthentication() { if (userAuthentication.isSelected()) { predefinedUsername.setDisable(false); predefinedPassword.setDisable(false); askForUsername.setDisable(false); askForPassword.setDisable(false); if (askForUsername.isSelected()) { username.setDisable(true); } else { username.setDisable(false); } if (askForPassword.isSelected()) { password.setDisable(true); } else { password.setDisable(false); } } else { username.setDisable(true); password.setDisable(true); predefinedUsername.setDisable(true); predefinedPassword.setDisable(true); askForUsername.setDisable(true); askForPassword.setDisable(true); } updateAuthIcon(userAuthentication.isSelected()); } @Override public void displayConnectionDetails(final ConfiguredConnectionDetails connection) { // Security userAuthentication.setSelected(connection.getUserAuthentication() != null && connection.getUserCredentials() != null); if (userAuthentication.isSelected()) { username.setText(connection.getUserCredentials().getUsername()); password.setText(BaseConfigurationUtils.decodePassword(connection.getUserCredentials().getPassword())); askForUsername.setSelected(connection.getUserAuthentication().isAskForUsername()); askForPassword.setSelected(connection.getUserAuthentication().isAskForPassword()); predefinedUsername.setSelected(!connection.getUserAuthentication().isAskForUsername()); predefinedPassword.setSelected(!connection.getUserAuthentication().isAskForPassword()); } else { username.setText(""); password.setText(""); predefinedUsername.setSelected(false); predefinedPassword.setSelected(false); askForUsername.setSelected(true); askForPassword.setSelected(true); } if (connection.getSSL() == null) { modeCombo.getSelectionModel().select(SecureSocketModeEnum.DISABLED); } else { propertiesPaneController.displayConnectionDetails(connection); modeCombo.getSelectionModel().select(connection.getSSL().getMode()); for (final String item : protocolCombo.getItems()) { if (item.equals(connection.getSSL().getProtocol())) { protocolCombo.getSelectionModel().select(item); break; } } certificatesPaneController.displayConnectionDetails(connection); keyStoresPaneController.displayConnectionDetails(connection); } showIcons(connection); updateUserAuthentication(); updateSSL(); } private void updateAuthIcon(boolean authEnabled) { final HBox authIcon = new HBox(); ConnectionController.createAuthIcon(authIcon, authEnabled, true); authTab.setGraphic(authIcon); } private void updateTlsIcon(boolean tlsEnabled) { final HBox tlsIcon = new HBox(); ConnectionController.createTlsIcon(tlsIcon, tlsEnabled, true); tlsTab.setGraphic(tlsIcon); } private void showIcons(final ConfiguredConnectionDetails connection) { updateTlsIcon(connection.getSSL() != null); updateAuthIcon(connection.getUserCredentials() != null); } // =============================== // === Setters and getters ======= // =============================== @Override public void setParent(final EditConnectionController controller) { this.parent = controller; } }