diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java index 6fef2b405..fc449a1ca 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -8,6 +8,7 @@ public enum FxmlFile { ADDVAULT_NEW_PASSWORD("/fxml/addvault_new_password.fxml"), // ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), // CHANGEPASSWORD("/fxml/changepassword.fxml"), // + FORGET_PASSWORD("/fxml/forget_password.fxml"), // MAIN_WINDOW("/fxml/main_window.fxml"), // MIGRATION_RUN("/fxml/migration_run.fxml"), // MIGRATION_START("/fxml/migration_start.fxml"), // diff --git a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPassword.java b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPassword.java new file mode 100644 index 000000000..ec6bd793b --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPassword.java @@ -0,0 +1,14 @@ +package org.cryptomator.ui.forgetPassword; + +import javax.inject.Qualifier; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface ForgetPassword { + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordComponent.java b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordComponent.java new file mode 100644 index 000000000..ddf310ab3 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordComponent.java @@ -0,0 +1,51 @@ +package org.cryptomator.ui.forgetPassword; + +import dagger.BindsInstance; +import dagger.Lazy; +import dagger.Subcomponent; +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; + +import javax.inject.Named; +import java.util.concurrent.CompletableFuture; + +@ForgetPasswordScoped +@Subcomponent(modules = {ForgetPasswordModule.class}) +public interface ForgetPasswordComponent { + + @ForgetPassword + ReadOnlyBooleanProperty confirmedProperty(); + + @ForgetPassword + Stage window(); + + @FxmlScene(FxmlFile.FORGET_PASSWORD) + Lazy scene(); + + default CompletableFuture showForgetPassword() { + CompletableFuture result = new CompletableFuture<>(); + Stage stage = window(); + stage.setScene(scene().get()); + stage.sizeToScene(); + stage.show(); + stage.setOnHidden(evt -> result.complete(confirmedProperty().get())); + return result; + } + + @Subcomponent.Builder + interface Builder { + + @BindsInstance + Builder vault(@ForgetPassword Vault vault); + + @BindsInstance + Builder owner(@Named("forgetPasswordOwner") Stage owner); + + ForgetPasswordComponent build(); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java new file mode 100644 index 000000000..c49e9aa27 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java @@ -0,0 +1,53 @@ +package org.cryptomator.ui.forgetPassword; + +import javafx.beans.property.BooleanProperty; +import javafx.fxml.FXML; +import javafx.stage.Stage; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.keychain.KeychainAccess; +import org.cryptomator.keychain.KeychainAccessException; +import org.cryptomator.ui.common.FxController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.util.Optional; + +@ForgetPasswordScoped +public class ForgetPasswordController implements FxController { + + private static final Logger LOG = LoggerFactory.getLogger(ForgetPasswordController.class); + + private final Stage window; + private final Vault vault; + private final Optional keychainAccess; + private final BooleanProperty confirmedResult; + + @Inject + public ForgetPasswordController(@ForgetPassword Stage window, @ForgetPassword Vault vault, Optional keychainAccess, @ForgetPassword BooleanProperty confirmedResult) { + this.window = window; + this.vault = vault; + this.keychainAccess = keychainAccess; + this.confirmedResult = confirmedResult; + } + + @FXML + public void close() { + window.close(); + } + + @FXML + public void finish() { + if (keychainAccess.isPresent()) { + try { + keychainAccess.get().deletePassphrase(vault.getId()); + LOG.debug("Forgot password for vault {}.", vault.getDisplayableName()); + confirmedResult.setValue(true); + } catch (KeychainAccessException e) { + LOG.error("Failed to remove entry from system keychain.", e); + confirmedResult.setValue(false); + } + } + window.close(); + } +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordModule.java b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordModule.java new file mode 100644 index 000000000..cce71b707 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordModule.java @@ -0,0 +1,74 @@ +package org.cryptomator.ui.forgetPassword; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoMap; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.stage.Modality; +import javafx.stage.Stage; +import org.cryptomator.ui.common.FXMLLoaderFactory; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.FxControllerKey; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; + +import javax.inject.Named; +import javax.inject.Provider; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; + +@Module +abstract class ForgetPasswordModule { + + @Provides + @ForgetPassword + @ForgetPasswordScoped + static FXMLLoaderFactory provideFxmlLoaderFactory(Map, Provider> factories, ResourceBundle resourceBundle) { + return new FXMLLoaderFactory(factories, resourceBundle); + } + + @Provides + @ForgetPassword + @ForgetPasswordScoped + static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcon") Optional windowIcon, @Named("forgetPasswordOwner") Stage owner) { + Stage stage = new Stage(); + stage.setTitle(resourceBundle.getString("forgetPassword.title")); + stage.setResizable(false); + stage.initModality(Modality.WINDOW_MODAL); + stage.initOwner(owner); + windowIcon.ifPresent(stage.getIcons()::add); + return stage; + } + + @Provides + @FxmlScene(FxmlFile.FORGET_PASSWORD) + @ForgetPasswordScoped + static Scene provideForgetPasswordScene(@ForgetPassword FXMLLoaderFactory fxmlLoaders, @ForgetPassword Stage window) { + return fxmlLoaders.createScene("/fxml/forget_password.fxml"); + } + + @Provides + @ForgetPassword + @ForgetPasswordScoped + static BooleanProperty provideConfirmedProperty() { + return new SimpleBooleanProperty(false); + } + + @Binds + @ForgetPassword + @ForgetPasswordScoped + abstract ReadOnlyBooleanProperty bindReadOnlyConfirmedProperty(@ForgetPassword BooleanProperty confirmedProperty); + + // ------------------ + + @Binds + @IntoMap + @FxControllerKey(ForgetPasswordController.class) + abstract FxController bindForgetPasswordController(ForgetPasswordController controller); +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordScoped.java b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordScoped.java new file mode 100644 index 000000000..388153e90 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordScoped.java @@ -0,0 +1,13 @@ +package org.cryptomator.ui.forgetPassword; + +import javax.inject.Scope; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Scope +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface ForgetPasswordScoped { + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java index 485ff7fce..4e6bbada1 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -12,13 +12,10 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.WritableValue; import javafx.fxml.FXML; import javafx.scene.Scene; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonType; import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; import javafx.stage.Stage; import javafx.util.Duration; -import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.cryptolib.api.InvalidPassphraseException; @@ -30,8 +27,7 @@ import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.Tasks; import org.cryptomator.ui.controls.NiceSecurePasswordField; -import org.cryptomator.ui.controls.SecurePasswordField; -import org.cryptomator.ui.util.DialogBuilderUtil; +import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,12 +51,13 @@ public class UnlockController implements FxController { private final Optional keychainAccess; private final ResourceBundle resourceBundle; private final Lazy successScene; + private final ForgetPasswordComponent.Builder forgetPassword; private final BooleanProperty unlockButtonDisabled; public NiceSecurePasswordField passwordField; public CheckBox savePassword; @Inject - public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, ExecutorService executor, Optional keychainAccess, ResourceBundle resourceBundle, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene) { + public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, ExecutorService executor, Optional keychainAccess, ResourceBundle resourceBundle, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, ForgetPasswordComponent.Builder forgetPassword) { this.window = window; this.vault = vault; this.executor = executor; @@ -68,6 +65,7 @@ public class UnlockController implements FxController { this.keychainAccess = keychainAccess; this.resourceBundle = resourceBundle; this.successScene = successScene; + this.forgetPassword = forgetPassword; this.unlockButtonDisabled = new SimpleBooleanProperty(); } @@ -128,21 +126,7 @@ public class UnlockController implements FxController { @FXML private void didClickSavePasswordCheckbox() { if (!savePassword.isSelected() && hasStoredPassword()) { - Alert confirmDialog = DialogBuilderUtil.buildConfirmationDialog( // - resourceBundle.getString("unlock.deleteSavedPasswordDialog.title"), // - resourceBundle.getString("unlock.deleteSavedPasswordDialog.header"), // - resourceBundle.getString("unlock.deleteSavedPasswordDialog.content"), // - SystemUtils.IS_OS_MAC_OSX ? ButtonType.CANCEL : ButtonType.OK); - Optional choice = confirmDialog.showAndWait(); - if (ButtonType.OK.equals(choice.get())) { - try { - keychainAccess.get().deletePassphrase(vault.getId()); - } catch (KeychainAccessException e) { - LOG.error("Failed to remove entry from system keychain.", e); - } - } else if (ButtonType.CANCEL.equals(choice.get())) { - savePassword.setSelected(true); - } + forgetPassword.vault(vault).build().showForgetPassword().thenAccept(forgotten -> savePassword.setSelected(!forgotten)); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java index 7bca6c4e4..7ee1f71e3 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java @@ -16,6 +16,7 @@ import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxControllerKey; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent; import javax.inject.Named; import javax.inject.Provider; @@ -23,7 +24,7 @@ import java.util.Map; import java.util.Optional; import java.util.ResourceBundle; -@Module +@Module(subcomponents={ForgetPasswordComponent.class}) abstract class UnlockModule { @Provides diff --git a/main/ui/src/main/resources/fxml/forget_password.fxml b/main/ui/src/main/resources/fxml/forget_password.fxml new file mode 100644 index 000000000..077a587d2 --- /dev/null +++ b/main/ui/src/main/resources/fxml/forget_password.fxml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +