/******************************************************************************* * Copyright (c) 2014, 2017 Sebastian Stenzel * This file is licensed under the terms of the MIT license. * See the LICENSE.txt file for more info. * * Contributors: * Sebastian Stenzel - initial API and implementation * Jean-Noël Charon - password strength meter ******************************************************************************/ package org.cryptomator.ui.controllers; import java.io.IOException; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.FileAlreadyExistsException; import java.util.Objects; import java.util.Optional; import javax.inject.Inject; import javax.inject.Singleton; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.l10n.Localization; import org.cryptomator.ui.model.Vault; import org.cryptomator.ui.util.PasswordStrengthUtil; import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javafx.application.Platform; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.GridPane; import javafx.scene.layout.Region; @Singleton public class InitializeController implements ViewController { private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class); private final Localization localization; private final PasswordStrengthUtil strengthRater; private final IntegerProperty passwordStrength = new SimpleIntegerProperty(); // 0-4 private Optional<InitializationListener> listener = Optional.empty(); private Vault vault; @Inject public InitializeController(Localization localization, PasswordStrengthUtil strengthRater) { this.localization = localization; this.strengthRater = strengthRater; } @FXML private SecPasswordField passwordField; @FXML private SecPasswordField retypePasswordField; @FXML private Button okButton; @FXML private Label messageLabel; @FXML private Label passwordStrengthLabel; @FXML private Region passwordStrengthLevel0; @FXML private Region passwordStrengthLevel1; @FXML private Region passwordStrengthLevel2; @FXML private Region passwordStrengthLevel3; @FXML private Region passwordStrengthLevel4; @FXML private GridPane root; @Override public void initialize() { BooleanBinding passwordIsEmpty = passwordField.textProperty().isEmpty(); BooleanBinding passwordsDiffer = passwordField.textProperty().isNotEqualTo(retypePasswordField.textProperty()); okButton.disableProperty().bind(passwordIsEmpty.or(passwordsDiffer)); passwordStrength.bind(EasyBind.map(passwordField.textProperty(), strengthRater::computeRate)); passwordStrengthLevel0.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(0), strengthRater::getBackgroundWithStrengthColor)); passwordStrengthLevel1.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(1), strengthRater::getBackgroundWithStrengthColor)); passwordStrengthLevel2.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(2), strengthRater::getBackgroundWithStrengthColor)); passwordStrengthLevel3.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(3), strengthRater::getBackgroundWithStrengthColor)); passwordStrengthLevel4.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(4), strengthRater::getBackgroundWithStrengthColor)); passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription)); } @Override public Parent getRoot() { return root; } void setVault(Vault vault) { this.vault = Objects.requireNonNull(vault); passwordField.swipe(); retypePasswordField.swipe(); // trigger "default" change to refresh key bindings: okButton.setDefaultButton(false); okButton.setDefaultButton(true); } // **************************************** // OK button // **************************************** @FXML protected void initializeVault(ActionEvent event) { final CharSequence passphrase = passwordField.getCharacters(); try { vault.create(passphrase); listener.ifPresent(this::invokeListenerLater); } catch (FileAlreadyExistsException ex) { messageLabel.setText(localization.getString("initialize.messageLabel.alreadyInitialized")); } catch (DirectoryNotEmptyException ex) { messageLabel.setText(localization.getString("initialize.messageLabel.notEmpty")); } catch (IOException ex) { LOG.error("I/O Exception", ex); messageLabel.setText(localization.getString("initialize.messageLabel.initializationFailed")); } finally { passwordField.swipe(); retypePasswordField.swipe(); } } /* Getter/Setter */ public InitializationListener getListener() { return listener.orElse(null); } public void setListener(InitializationListener listener) { this.listener = Optional.ofNullable(listener); } /* callback */ private void invokeListenerLater(InitializationListener listener) { Platform.runLater(() -> { listener.didInitialize(); }); } @FunctionalInterface interface InitializationListener { void didInitialize(); } }