From 88220cabee810ce0a7e38ca065cf08e06d5b3f65 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 30 Aug 2019 10:59:37 +0200 Subject: [PATCH] Added access location readme file --- .../CreateNewVaultPasswordController.java | 98 ++++++++++++++----- .../ui/addvaultwizard/ReadmeGenerator.java | 8 ++ .../resources/fxml/addvault_new_password.fxml | 7 +- .../main/resources/i18n/strings.properties | 7 +- 4 files changed, 94 insertions(+), 26 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java index 47d4a5653..c0c169184 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java @@ -3,24 +3,30 @@ package org.cryptomator.ui.addvaultwizard; import dagger.Lazy; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; +import javafx.beans.binding.ObjectBinding; +import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.Scene; -import javafx.scene.control.Button; import javafx.scene.control.CheckBox; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.stage.Stage; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultFactory; +import org.cryptomator.cryptofs.CryptoFileSystemProperties; +import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.common.Tasks; import org.cryptomator.ui.controls.FontAwesome5IconView; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.util.PasswordStrengthUtil; @@ -30,23 +36,28 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.io.IOException; -import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; -import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.Collections; import java.util.ResourceBundle; +import java.util.concurrent.ExecutorService; + +import static java.nio.charset.StandardCharsets.US_ASCII; @AddVaultWizardScoped public class CreateNewVaultPasswordController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultPasswordController.class); + private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; private final Stage window; private final Lazy chooseLocationScene; private final Lazy successScene; + private final ExecutorService executor; private final StringProperty vaultName; private final ObjectProperty vaultPath; private final ObservableList vaults; @@ -56,8 +67,10 @@ public class CreateNewVaultPasswordController implements FxController { private final PasswordStrengthUtil strengthRater; private final ReadmeGenerator readmeGenerator; private final IntegerProperty passwordStrength; + private final BooleanProperty processing; + private final BooleanProperty readyToCreateVault; + private final ObjectBinding createVaultButtonState; - public Button finishButton; public SecPasswordField passwordField; public SecPasswordField reenterField; public Label passwordStrengthLabel; @@ -68,10 +81,11 @@ public class CreateNewVaultPasswordController implements FxController { public CheckBox finalConfirmationCheckbox; @Inject - CreateNewVaultPasswordController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, StringProperty vaultName, ObjectProperty vaultPath, ObservableList vaults, @AddVaultWizard ObjectProperty vault, VaultFactory vaultFactory, ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, ReadmeGenerator readmeGenerator) { + CreateNewVaultPasswordController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, ExecutorService executor, StringProperty vaultName, ObjectProperty vaultPath, ObservableList vaults, @AddVaultWizard ObjectProperty vault, VaultFactory vaultFactory, ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, ReadmeGenerator readmeGenerator) { this.window = window; this.chooseLocationScene = chooseLocationScene; this.successScene = successScene; + this.executor = executor; this.vaultName = vaultName; this.vaultPath = vaultPath; this.vaults = vaults; @@ -81,6 +95,9 @@ public class CreateNewVaultPasswordController implements FxController { this.strengthRater = strengthRater; this.readmeGenerator = readmeGenerator; this.passwordStrength = new SimpleIntegerProperty(-1); + this.processing = new SimpleBooleanProperty(); + this.readyToCreateVault = new SimpleBooleanProperty(); + this.createVaultButtonState = Bindings.createObjectBinding(this::getCreateVaultButtonState, processing); } @FXML @@ -90,8 +107,7 @@ public class CreateNewVaultPasswordController implements FxController { //binding indicating if the passwords not match BooleanBinding passwordsMatch = Bindings.createBooleanBinding(() -> CharSequence.compare(passwordField.getCharacters(), reenterField.getCharacters()) == 0, passwordField.textProperty(), reenterField.textProperty()); BooleanBinding reenterFieldNotEmpty = reenterField.textProperty().isNotEmpty(); - //disable the finish button when passwords do not match or one is empty - finishButton.disableProperty().bind(reenterFieldNotEmpty.not().or(passwordsMatch.not()).or(finalConfirmationCheckbox.selectedProperty().not())); + readyToCreateVault.bind(reenterFieldNotEmpty.and(passwordsMatch).and(finalConfirmationCheckbox.selectedProperty()).and(processing.not())); //make match indicator invisible when passwords do not match or one is empty passwordMatchBox.visibleProperty().bind(reenterFieldNotEmpty); checkmark.visibleProperty().bind(passwordsMatch.and(reenterFieldNotEmpty)); @@ -111,35 +127,53 @@ public class CreateNewVaultPasswordController implements FxController { @FXML public void next() { - VaultSettings vaultSettings = VaultSettings.withRandomId(); - vaultSettings.path().setValue(vaultPath.get()); - Vault newVault = vaultFactory.get(vaultSettings); try { - Files.createDirectory(vaultSettings.path().get()); + Files.createDirectory(vaultPath.get()); } catch (FileAlreadyExistsException e) { - // TODO show specific error screen - LOG.error("", e); + LOG.error("Vault dir already exists.", e); + window.setScene(chooseLocationScene.get()); } catch (IOException e) { // TODO show generic error screen LOG.error("", e); } - try { - newVault.create(passwordField.getCharacters()); - LOG.info("Created new vault at path {}", vaultPath.get()); - String readmeFileName = resourceBundle.getString("addvault.new.readme.storageLocation.fileName"); - Path readmeFile = vaultPath.get().resolve(readmeFileName); - try (WritableByteChannel ch = Files.newByteChannel(readmeFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) { - ByteBuffer buf = StandardCharsets.US_ASCII.encode(readmeGenerator.createVaultStorageLocationReadmeRtf()); - ch.write(buf); - } - LOG.info("Created readme file {}", readmeFile); + + processing.set(true); + Tasks.create(() -> { + initializeVault(vaultPath.get(), passwordField.getCharacters()); + }).onSuccess(() -> { + VaultSettings vaultSettings = VaultSettings.withRandomId(); + vaultSettings.path().setValue(vaultPath.get()); + Vault newVault = vaultFactory.get(vaultSettings); vault.set(newVault); vaults.add(newVault); window.setScene(successScene.get()); - } catch (IOException e) { + }).onError(IOException.class, e -> { // TODO show generic error screen LOG.error("", e); + }).andFinally(() -> { + processing.set(false); + }).runOnce(executor); + } + + private void initializeVault(Path path, CharSequence passphrase) throws IOException { + CryptoFileSystemProvider.initialize(path, MASTERKEY_FILENAME, passphrase); + CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() // + .withPassphrase(passphrase) // + .withFlags(Collections.emptySet()) // + .withMasterkeyFilename(MASTERKEY_FILENAME) // + .build(); + + String vaultReadmeFileName = resourceBundle.getString("addvault.new.readme.accessLocation.fileName"); + try (FileSystem fs = CryptoFileSystemProvider.newFileSystem(path, fsProps); // + WritableByteChannel ch = Files.newByteChannel(fs.getPath("/", vaultReadmeFileName), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) { + ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf())); } + + String storagePathReadmeFileName = resourceBundle.getString("addvault.new.readme.storageLocation.fileName"); + try (WritableByteChannel ch = Files.newByteChannel(path.resolve(storagePathReadmeFileName), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) { + ch.write(US_ASCII.encode(readmeGenerator.createVaultStorageLocationReadmeRtf())); + } + LOG.info("Created vault at {}", path); } /* Getter/Setter */ @@ -159,4 +193,20 @@ public class CreateNewVaultPasswordController implements FxController { public int getPasswordStrength() { return passwordStrength.get(); } + + public BooleanProperty readyToCreateVaultProperty() { + return readyToCreateVault; + } + + public boolean isReadyToCreateVault() { + return readyToCreateVault.get(); + } + + public ObjectBinding createVaultButtonStateProperty() { + return createVaultButtonState; + } + + public ContentDisplay getCreateVaultButtonState() { + return processing.get() ? ContentDisplay.LEFT : ContentDisplay.TEXT_ONLY; + } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java index fe432a4b1..543e84573 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java @@ -27,6 +27,14 @@ public class ReadmeGenerator { String.format(resourceBundle.getString("addvault.new.readme.storageLocation.4"), HELP_URL) // )); } + + public String createVaultAccessLocationReadmeRtf() { + return createDocument(List.of( // + resourceBundle.getString("addvault.new.readme.accessLocation.1"), // + resourceBundle.getString("addvault.new.readme.accessLocation.2"), // + resourceBundle.getString("addvault.new.readme.accessLocation.3") // + )); + } // visible for testing String createDocument(Iterable paragraphs) { diff --git a/main/ui/src/main/resources/fxml/addvault_new_password.fxml b/main/ui/src/main/resources/fxml/addvault_new_password.fxml index c58489e3e..1c6fd1f0d 100644 --- a/main/ui/src/main/resources/fxml/addvault_new_password.fxml +++ b/main/ui/src/main/resources/fxml/addvault_new_password.fxml @@ -11,6 +11,7 @@ + diff --git a/main/ui/src/main/resources/i18n/strings.properties b/main/ui/src/main/resources/i18n/strings.properties index 99eee930a..00534b0bc 100644 --- a/main/ui/src/main/resources/i18n/strings.properties +++ b/main/ui/src/main/resources/i18n/strings.properties @@ -39,10 +39,15 @@ addvaultwizard.new.passwordsMatch=Passwords match! addvaultwizard.new.passwordsDoNotMatch=Passwords do not match addvaultwizard.new.finalConfirmation=I understand that I will not be able to recover my data if I forget my password addvault.new.readme.storageLocation.fileName=WHAT IS THIS DIRECTORY.rtf -addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ VAULT FILES ⚠️ +addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ VAULT FILES ⚠️ addvault.new.readme.storageLocation.2=This is your vault's storage location. {\\b DO NOT} alter any files within this directory. addvault.new.readme.storageLocation.3=If you want to encrypt files using Cryptomator, unlock the vault and use the provided drive. addvault.new.readme.storageLocation.4=If you need help, try %s. +addvault.new.readme.accessLocation.fileName=WELCOME TO YOUR VAULT.rtf +addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ENCRYPTED VOLUME 🔐️ +addvault.new.readme.accessLocation.2=This is your vault's access location. Any files added to this volume will be encrypted by Cryptomator. To access this volume at a later point in time, simply unlock it again from within the Cryptomator application. +addvault.new.readme.accessLocation.3=Feel free to remove this file. + ## Existing addvaultwizard.existing.instruction=Choose the "masterkey.cryptomator" file of your existing vault. addvaultwizard.existing.chooseBtn=Choose…