move migration to KeychainManager

This commit is contained in:
Armin Schrenk
2025-02-25 17:33:14 +01:00
parent 0bcbf9a13a
commit eb0e630a44
2 changed files with 35 additions and 28 deletions

View File

@@ -2,8 +2,11 @@ package org.cryptomator.common.keychain;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.cryptomator.common.Passphrase;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -13,11 +16,14 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Singleton
public class KeychainManager implements KeychainAccessProvider {
private static final Logger LOG = LoggerFactory.getLogger(KeychainManager.class);
private final ObjectExpression<KeychainAccessProvider> keychain;
private final LoadingCache<String, BooleanProperty> passphraseStoredProperties;
private final ReentrantReadWriteLock lock;
@@ -169,4 +175,24 @@ public class KeychainManager implements KeychainAccessProvider {
return this.keychain;
}
public static void migrate(KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider, Map<String, String> idsAndNames) {
if (oldProvider instanceof KeychainManager || newProvider instanceof KeychainManager) {
throw new IllegalArgumentException("KeychainManger must not be the source or target of migration");
}
LOG.info("Migrating keychain entries {} from {} to {}", idsAndNames.keySet(), oldProvider.displayName(), newProvider.displayName());
idsAndNames.forEach((id, name) -> {
try {
var passphrase = oldProvider.loadPassphrase(id);
if (passphrase != null) {
var wrapper = new Passphrase(passphrase);
newProvider.storePassphrase(id, name, wrapper);
oldProvider.deletePassphrase(id);
wrapper.destroy();
}
} catch (KeychainAccessException e) {
LOG.error("Failed to migrate keychain entry for vault {}.", id, e);
}
});
}
}

View File

@@ -2,13 +2,11 @@ package org.cryptomator.ui.preferences;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.Passphrase;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
import org.cryptomator.integrations.common.NamedServiceProvider;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.integrations.quickaccess.QuickAccessService;
import org.cryptomator.ui.common.FxController;
@@ -31,6 +29,7 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
@PreferencesScoped
public class GeneralPreferencesController implements FxController {
@@ -89,7 +88,7 @@ public class GeneralPreferencesController implements FxController {
Bindings.bindBidirectional(settings.keychainProvider, keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter);
useKeychainCheckbox.selectedProperty().bindBidirectional(settings.useKeychain);
keychainBackendChoiceBox.disableProperty().bind(useKeychainCheckbox.selectedProperty().not());
keychainBackendChoiceBox.valueProperty().addListener(this::migrateMacKeychainEntries);
keychainBackendChoiceBox.valueProperty().addListener(this::migrateKeychainEntries);
useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess);
var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices);
@@ -100,32 +99,14 @@ public class GeneralPreferencesController implements FxController {
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
}
public void migrateMacKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
if (!SystemUtils.IS_OS_MAC) {
return;
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
//currently, we only migrate on macOS (touchID vs regular keychain)
if (SystemUtils.IS_OS_MAC) {
var idsAndNames = settings.directories.stream().collect(Collectors.toMap(vs -> vs.id, vs -> vs.displayName.getValue()));
if (!idsAndNames.isEmpty()) {
keychainMigrations = keychainMigrations.thenRunAsync(() -> KeychainManager.migrate(oldProvider, newProvider, idsAndNames), backgroundExecutor);
}
}
record VIdAndName(String id, String name) {}
var vaults = settings.directories.stream().map(vault -> new VIdAndName(vault.id, vault.displayName.getValue())).toList();
keychainMigrations = keychainMigrations.thenRunAsync(() -> {
if (!vaults.isEmpty()) {
LOG.info("Migrating keychain entries for vaults: {}", vaults.stream().map(VIdAndName::id));
}
for (var v : vaults) { //TODO: migrate to pattern matching once supported
try {
var passphrase = oldProvider.loadPassphrase(v.id);
if (passphrase != null) {
var wrapper = new Passphrase(passphrase);
newProvider.storePassphrase(v.id, v.name, wrapper);
oldProvider.deletePassphrase(v.id);
wrapper.destroy();
}
} catch (KeychainAccessException e) {
LOG.error("Failed to migrate keychain entry for vault {}.", v.id, e);
}
}
}, backgroundExecutor);
}
public boolean isAutoStartSupported() {