diff --git a/src/main/java/org/cryptomator/ui/health/StartController.java b/src/main/java/org/cryptomator/ui/health/StartController.java index 5dc357a8c..e9aded0d5 100644 --- a/src/main/java/org/cryptomator/ui/health/StartController.java +++ b/src/main/java/org/cryptomator/ui/health/StartController.java @@ -6,7 +6,6 @@ import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.VaultConfigLoadException; import org.cryptomator.cryptofs.VaultKeyInvalidException; import org.cryptomator.cryptolib.api.Masterkey; -import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; @@ -73,9 +72,15 @@ public class StartController implements FxController { private void loadKey() { assert !Platform.isFxApplicationThread(); assert unverifiedVaultConfig.get() != null; + try { + keyLoadingStrategy.use(this::verifyVaultConfig); + } catch (VaultConfigLoadException e) { + throw new LoadingFailedException(e); + } + } + + private void verifyVaultConfig(KeyLoadingStrategy keyLoadingStrategy) throws VaultConfigLoadException { var unverifiedCfg = unverifiedVaultConfig.get(); - // TODO: dedup keyloading w/ UnlockWorkflow.attemptUnlock() - boolean success = false; try (var masterkey = keyLoadingStrategy.loadKey(unverifiedCfg.getKeyId())) { var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion()); vaultConfigRef.set(verifiedCfg); @@ -83,18 +88,6 @@ public class StartController implements FxController { if (old != null) { old.destroy(); } - success = true; - } catch (MasterkeyLoadingFailedException e) { - if (keyLoadingStrategy.recoverFromException(e)) { - // retry - loadKey(); - } else { - throw new LoadingFailedException(e); - } - } catch (VaultConfigLoadException e) { - throw new LoadingFailedException(e); - } finally { - keyLoadingStrategy.cleanup(success); } } diff --git a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java index eaef7480b..614247ebc 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java @@ -3,6 +3,8 @@ package org.cryptomator.ui.keyloading; import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.net.URI; @@ -12,6 +14,8 @@ import java.net.URI; @FunctionalInterface public interface KeyLoadingStrategy extends MasterkeyLoader { + Logger LOG = LoggerFactory.getLogger(KeyLoadingStrategy.class); + /** * Loads a master key. This might be a long-running operation, as it may require user input or expensive computations. *

@@ -60,4 +64,36 @@ public interface KeyLoadingStrategy extends MasterkeyLoader { }; } + /** + * Makes the given user apply this key loading strategy. If the user fails with a {@link MasterkeyLoadingFailedException}, + * an attempt is made to {@link #recoverFromException(MasterkeyLoadingFailedException) recover} from it. Any other exception will be rethrown. + * + * @param user Some method using this strategy. May be invoked multiple times in case of recoverable {@link MasterkeyLoadingFailedException}s + * @param Optional exception type thrown by user + * @throws MasterkeyLoadingFailedException If a non-recoverable exception is thrown by user + * @throws E Exception thrown by user and rethrown by this method + */ + default void use(KeyLoadingStrategyUser user) throws MasterkeyLoadingFailedException, E { + boolean success = false; + try { + user.use(this); + } catch (MasterkeyLoadingFailedException e) { + if (recoverFromException(e)) { + LOG.info("Unlock attempt threw {}. Reattempting...", e.getClass().getSimpleName()); + use(user); + } else { + throw e; + } + } finally { + cleanup(success); + } + } + + @FunctionalInterface + interface KeyLoadingStrategyUser { + + void use(KeyLoadingStrategy strategy) throws MasterkeyLoadingFailedException, E; + + } + } diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java index 4ff50814d..073258d80 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.unlock; +import com.google.common.base.Throwables; import dagger.Lazy; import org.cryptomator.common.mountpoint.InvalidMountPointException; import org.cryptomator.common.vaults.MountPointRequirement; @@ -7,12 +8,10 @@ import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.Volume.VolumeException; import org.cryptomator.cryptolib.api.CryptoException; -import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.VaultService; -import org.cryptomator.ui.keyloading.KeyLoadingComponent; import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,20 +67,14 @@ public class UnlockWorkflow extends Task { } private void attemptUnlock() throws IOException, VolumeException, InvalidMountPointException, CryptoException { - // TODO: dedup keyloading w/ StartController.loadKey() - boolean success = false; try { - vault.unlock(keyLoadingStrategy); - success = true; - } catch (MasterkeyLoadingFailedException e) { - if (keyLoadingStrategy.recoverFromException(e)) { - LOG.info("Unlock attempt threw {}. Reattempting...", e.getClass().getSimpleName()); - attemptUnlock(); - } else { - throw e; - } - } finally { - keyLoadingStrategy.cleanup(success); + keyLoadingStrategy.use(vault::unlock); + } catch (Exception e) { + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, VolumeException.class); + Throwables.propagateIfPossible(e, InvalidMountPointException.class); + Throwables.propagateIfPossible(e, CryptoException.class); + throw new IllegalStateException("unexpected exception type", e); } }