mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-22 07:54:19 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fb987607c | ||
|
|
5d4066d29a | ||
|
|
d3909134d5 | ||
|
|
fd9d1bf0cf | ||
|
|
2f812377c3 | ||
|
|
ebce90eb74 | ||
|
|
c888b52ebb | ||
|
|
e422c7ce4b | ||
|
|
41ad8d5dcc | ||
|
|
526c3973d6 | ||
|
|
dc16c961af | ||
|
|
c54a603078 | ||
|
|
be59097a53 | ||
|
|
ccc4842839 | ||
|
|
d95625faa1 | ||
|
|
c5bfdf32a9 | ||
|
|
0598681a23 | ||
|
|
3b8bff0d47 | ||
|
|
55051072cb | ||
|
|
6cb409eeaf | ||
|
|
b38a350c48 | ||
|
|
0e5857015a | ||
|
|
9e9aa20692 | ||
|
|
ad49ca2cb4 | ||
|
|
ba84da71f3 | ||
|
|
5c34f59d09 | ||
|
|
7499867665 | ||
|
|
7b293b8082 | ||
|
|
46e8a13555 | ||
|
|
63a3150ec3 | ||
|
|
0a78987d30 | ||
|
|
929782c318 | ||
|
|
97a0f9c435 | ||
|
|
f7e65f4eec | ||
|
|
b3c56f3aab | ||
|
|
b5dbfd3209 | ||
|
|
78675c9638 | ||
|
|
e8126e68ce | ||
|
|
c8075dbc19 | ||
|
|
2f1b5109d6 | ||
|
|
aa34ad52e6 | ||
|
|
175ed500a2 | ||
|
|
af3779ba2e | ||
|
|
953aee560f | ||
|
|
d8b798ff0f | ||
|
|
9cc863ae79 | ||
|
|
14ba852351 | ||
|
|
9516928529 | ||
|
|
62827b69cc | ||
|
|
2378227756 | ||
|
|
2194360c8a | ||
|
|
10bce1fb06 | ||
|
|
cfa3093dd0 |
@@ -130,6 +130,7 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
||||||
|
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json\""
|
||||||
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
|
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
|
||||||
--resource-dir dist/win/resources
|
--resource-dir dist/win/resources
|
||||||
--icon dist/win/resources/Cryptomator.ico
|
--icon dist/win/resources/Cryptomator.ico
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator" -Dcryptomator.pluginDir="@{appdata}/Cryptomator/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator" -Dcryptomator.pluginDir="@{appdata}/Cryptomator/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.integrationsWin.windowsHelloKeychainPaths="@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator-Dev" -Dcryptomator.pluginDir="@{appdata}/Cryptomator-Dev/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator-Dev" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator-Dev" -Dcryptomator.pluginDir="@{appdata}/Cryptomator-Dev/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.integrationsWin.windowsHelloKeychainPaths="@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator-Dev" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -35,8 +35,8 @@
|
|||||||
<!-- cryptomator dependencies -->
|
<!-- cryptomator dependencies -->
|
||||||
<cryptomator.cryptofs.version>2.7.1</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>2.7.1</cryptomator.cryptofs.version>
|
||||||
<cryptomator.integrations.version>1.4.0</cryptomator.integrations.version>
|
<cryptomator.integrations.version>1.4.0</cryptomator.integrations.version>
|
||||||
<cryptomator.integrations.win.version>1.3.0</cryptomator.integrations.win.version>
|
<cryptomator.integrations.win.version>1.4.1</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.mac.version>1.2.4</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.mac.version>1.3.0</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.linux.version>1.5.1</cryptomator.integrations.linux.version>
|
<cryptomator.integrations.linux.version>1.5.1</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.fuse.version>5.0.2</cryptomator.fuse.version>
|
<cryptomator.fuse.version>5.0.2</cryptomator.fuse.version>
|
||||||
<cryptomator.webdav.version>2.0.7</cryptomator.webdav.version>
|
<cryptomator.webdav.version>2.0.7</cryptomator.webdav.version>
|
||||||
@@ -201,6 +201,12 @@
|
|||||||
<version>2.0.1</version>
|
<version>2.0.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Caffeine -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
<version>3.1.8</version>
|
||||||
|
</dependency>
|
||||||
<!-- JUnit / Mockito / Hamcrest -->
|
<!-- JUnit / Mockito / Hamcrest -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ open module org.cryptomator.desktop {
|
|||||||
requires jakarta.inject;
|
requires jakarta.inject;
|
||||||
requires static javax.inject;
|
requires static javax.inject;
|
||||||
requires java.compiler;
|
requires java.compiler;
|
||||||
|
requires com.github.benmanes.caffeine;
|
||||||
|
|
||||||
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package org.cryptomator;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class JavaFXUtil {
|
||||||
|
|
||||||
|
public static boolean startPlatform() throws InterruptedException {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
try {
|
||||||
|
Platform.startup(latch::countDown);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
//already initialized
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
return latch.await(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ public class Environment {
|
|||||||
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
||||||
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
||||||
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
||||||
|
private static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
|
||||||
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
||||||
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
||||||
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
||||||
@@ -45,6 +46,7 @@ public class Environment {
|
|||||||
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
|
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
|
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
|
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
|
||||||
|
logCryptomatorSystemProperty(WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
|
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
|
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
|
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
|
||||||
@@ -85,6 +87,10 @@ public class Environment {
|
|||||||
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
|
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stream<Path> getWindowsHelloKeychainPath() {
|
||||||
|
return getPaths(WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
public Stream<Path> getP12Path() {
|
public Stream<Path> getP12Path() {
|
||||||
return getPaths(P12_PATH_PROP_NAME);
|
return getPaths(P12_PATH_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.cryptomator.common.keychain;
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
|
|
||||||
@@ -24,9 +23,9 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
@Inject
|
@Inject
|
||||||
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
|
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
|
||||||
this.keychain = selectedKeychain;
|
this.keychain = selectedKeychain;
|
||||||
this.passphraseStoredProperties = CacheBuilder.newBuilder() //
|
this.passphraseStoredProperties = Caffeine.newBuilder() //
|
||||||
.weakValues() //
|
.softValues() //
|
||||||
.build(CacheLoader.from(this::createStoredPassphraseProperty));
|
.build(this::createStoredPassphraseProperty);
|
||||||
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
|
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,8 +43,13 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean ignored) throws KeychainAccessException {
|
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
|
||||||
getKeychainOrFail().storePassphrase(key, displayName, passphrase);
|
storePassphrase(key, displayName, passphrase, true); //TODO: currently only TouchID is using this parameter, so this is okayish
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean requireOsAuthentication) throws KeychainAccessException {
|
||||||
|
getKeychainOrFail().storePassphrase(key, displayName, passphrase, requireOsAuthentication);
|
||||||
setPassphraseStored(key, true);
|
setPassphraseStored(key, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,15 +106,13 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setPassphraseStored(String key, boolean value) {
|
private void setPassphraseStored(String key, boolean value) {
|
||||||
BooleanProperty property = passphraseStoredProperties.getIfPresent(key);
|
BooleanProperty property = passphraseStoredProperties.get(key, _ -> new SimpleBooleanProperty(value));
|
||||||
if (property != null) {
|
|
||||||
if (Platform.isFxApplicationThread()) {
|
if (Platform.isFxApplicationThread()) {
|
||||||
property.set(value);
|
property.set(value);
|
||||||
} else {
|
} else {
|
||||||
Platform.runLater(() -> property.set(value));
|
Platform.runLater(() -> property.set(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable property for use in the UI that tells whether a passphrase is stored for the given key.
|
* Returns an observable property for use in the UI that tells whether a passphrase is stored for the given key.
|
||||||
@@ -124,7 +126,7 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
* @see #isPassphraseStored(String)
|
* @see #isPassphraseStored(String)
|
||||||
*/
|
*/
|
||||||
public ReadOnlyBooleanProperty getPassphraseStoredProperty(String key) {
|
public ReadOnlyBooleanProperty getPassphraseStoredProperty(String key) {
|
||||||
return passphraseStoredProperties.getUnchecked(key);
|
return passphraseStoredProperties.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BooleanProperty createStoredPassphraseProperty(String key) {
|
private BooleanProperty createStoredPassphraseProperty(String key) {
|
||||||
@@ -135,4 +137,8 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObjectExpression<KeychainAccessProvider> getKeychainImplementation() {
|
||||||
|
return this.keychain;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -112,14 +112,14 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void savePasswordToSystemkeychain(Passphrase passphrase) {
|
private void savePasswordToSystemkeychain(Passphrase passphrase) {
|
||||||
if (keychain.isSupported()) {
|
|
||||||
try {
|
try {
|
||||||
|
if (keychain.isSupported() && !keychain.getPassphraseStoredProperty(vault.getId()).getValue()) {
|
||||||
keychain.storePassphrase(vault.getId(), vault.getDisplayName(), passphrase);
|
keychain.storePassphrase(vault.getId(), vault.getDisplayName(), passphrase);
|
||||||
|
}
|
||||||
} catch (KeychainAccessException e) {
|
} catch (KeychainAccessException e) {
|
||||||
LOG.error("Failed to store passphrase in system keychain.", e);
|
LOG.error("Failed to store passphrase in system keychain.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private Path askUserForMasterkeyFilePath() throws InterruptedException {
|
private Path askUserForMasterkeyFilePath() throws InterruptedException {
|
||||||
var comp = masterkeyFileChoice.build();
|
var comp = masterkeyFileChoice.build();
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
|||||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
@@ -21,7 +21,6 @@ public class VaultDetailLockedController implements FxController {
|
|||||||
private final ReadOnlyObjectProperty<Vault> vault;
|
private final ReadOnlyObjectProperty<Vault> vault;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||||
private final KeychainManager keychain;
|
|
||||||
private final Stage mainWindow;
|
private final Stage mainWindow;
|
||||||
private final ObservableValue<Boolean> passwordSaved;
|
private final ObservableValue<Boolean> passwordSaved;
|
||||||
|
|
||||||
@@ -30,13 +29,11 @@ public class VaultDetailLockedController implements FxController {
|
|||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||||
this.keychain = keychain;
|
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
if (keychain.isSupported() && !keychain.isLocked()) {
|
this.passwordSaved = Bindings.createBooleanBinding(() -> {
|
||||||
this.passwordSaved = vault.flatMap(v -> keychain.getPassphraseStoredProperty(v.getId())).orElse(false);
|
var v = vault.get();
|
||||||
} else {
|
return v != null && keychain.getPassphraseStoredProperty(v.getId()).getValue();
|
||||||
this.passwordSaved = new SimpleBooleanProperty(false);
|
}, vault, keychain.getKeychainImplementation());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
package org.cryptomator.ui.preferences;
|
package org.cryptomator.ui.preferences;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Environment;
|
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.common.settings.Settings;
|
||||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||||
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
|
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
|
||||||
import org.cryptomator.integrations.common.NamedServiceProvider;
|
import org.cryptomator.integrations.common.NamedServiceProvider;
|
||||||
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
import org.cryptomator.integrations.quickaccess.QuickAccessService;
|
import org.cryptomator.integrations.quickaccess.QuickAccessService;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
@@ -14,6 +18,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
@@ -36,6 +41,7 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
private final Application application;
|
private final Application application;
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
private final List<KeychainAccessProvider> keychainAccessProviders;
|
private final List<KeychainAccessProvider> keychainAccessProviders;
|
||||||
|
private final KeychainManager keychain;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
public CheckBox useKeychainCheckbox;
|
public CheckBox useKeychainCheckbox;
|
||||||
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
|
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
|
||||||
@@ -48,11 +54,12 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
public ToggleGroup nodeOrientation;
|
public ToggleGroup nodeOrientation;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, Application application, Environment environment, FxApplicationWindows appWindows) {
|
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, Environment environment, FxApplicationWindows appWindows) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.autoStartProvider = autoStartProvider;
|
this.autoStartProvider = autoStartProvider;
|
||||||
this.keychainAccessProviders = keychainAccessProviders;
|
this.keychainAccessProviders = keychainAccessProviders;
|
||||||
|
this.keychain = keychain;
|
||||||
this.quickAccessServices = QuickAccessService.get().toList();
|
this.quickAccessServices = QuickAccessService.get().toList();
|
||||||
this.application = application;
|
this.application = application;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
@@ -73,6 +80,7 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
Bindings.bindBidirectional(settings.keychainProvider, keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter);
|
Bindings.bindBidirectional(settings.keychainProvider, keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter);
|
||||||
useKeychainCheckbox.selectedProperty().bindBidirectional(settings.useKeychain);
|
useKeychainCheckbox.selectedProperty().bindBidirectional(settings.useKeychain);
|
||||||
keychainBackendChoiceBox.disableProperty().bind(useKeychainCheckbox.selectedProperty().not());
|
keychainBackendChoiceBox.disableProperty().bind(useKeychainCheckbox.selectedProperty().not());
|
||||||
|
keychainBackendChoiceBox.valueProperty().addListener(this::migrateKeychainEntriesOnMac);
|
||||||
|
|
||||||
useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess);
|
useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess);
|
||||||
var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices);
|
var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices);
|
||||||
@@ -83,6 +91,35 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
|
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void migrateKeychainEntriesOnMac(Observable observable) {
|
||||||
|
if (!SystemUtils.IS_OS_MAC) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var provider = keychainBackendChoiceBox.getSelectionModel().getSelectedItem();
|
||||||
|
var providerId = "org.cryptomator.macos.keychain.MacSystemKeychainAccess";
|
||||||
|
var isSystemKeychain = provider.getClass().getName().equals(providerId);
|
||||||
|
|
||||||
|
List<String> vaults = settings.directories.stream()
|
||||||
|
.map(vault -> vault.id)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (!vaults.isEmpty()) {
|
||||||
|
LOG.info("Migrating keychain entries for vaults: {}", vaults);
|
||||||
|
}
|
||||||
|
for (String vaultId :vaults) {
|
||||||
|
try {
|
||||||
|
if (keychain.isPassphraseStored(vaultId)) {
|
||||||
|
var passphrase = keychain.loadPassphrase(vaultId);
|
||||||
|
keychain.deletePassphrase(vaultId);
|
||||||
|
keychain.storePassphrase(vaultId, vaultId, new Passphrase(passphrase), !isSystemKeychain);
|
||||||
|
}
|
||||||
|
} catch (KeychainAccessException e) {
|
||||||
|
LOG.error("Failed to migrate keychain entries.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAutoStartSupported() {
|
public boolean isAutoStartSupported() {
|
||||||
return autoStartProvider.isPresent();
|
return autoStartProvider.isPresent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,13 @@ public class EnvironmentTest {
|
|||||||
env.getKeychainPath();
|
env.getKeychainPath();
|
||||||
Mockito.verify(env).getPaths("cryptomator.integrationsWin.keychainPaths");
|
Mockito.verify(env).getPaths("cryptomator.integrationsWin.keychainPaths");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWindowsHelloKeychainPath() {
|
||||||
|
Mockito.doReturn(Stream.of()).when(env).getPaths(Mockito.anyString());
|
||||||
|
env.getWindowsHelloKeychainPath();
|
||||||
|
Mockito.verify(env).getPaths("cryptomator.integrationsWin.windowsHelloKeychainPaths");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.cryptomator.common.keychain;
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
|
|
||||||
|
import org.cryptomator.JavaFXUtil;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Assumptions;
|
import org.junit.jupiter.api.Assumptions;
|
||||||
@@ -19,6 +20,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
public class KeychainManagerTest {
|
public class KeychainManagerTest {
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void startup() throws InterruptedException {
|
||||||
|
var isRunning = JavaFXUtil.startPlatform();
|
||||||
|
Assumptions.assumeTrue(isRunning);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStoreAndLoad() throws KeychainAccessException {
|
public void testStoreAndLoad() throws KeychainAccessException {
|
||||||
KeychainManager keychainManager = new KeychainManager(new SimpleObjectProperty<>(new MapKeychainAccess()));
|
KeychainManager keychainManager = new KeychainManager(new SimpleObjectProperty<>(new MapKeychainAccess()));
|
||||||
@@ -27,15 +34,7 @@ public class KeychainManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
public static class WhenObservingProperties {
|
public class WhenObservingProperties {
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
public static void startup() throws InterruptedException {
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
Platform.startup(latch::countDown);
|
|
||||||
var javafxStarted = latch.await(5, TimeUnit.SECONDS);
|
|
||||||
Assumptions.assumeTrue(javafxStarted);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPropertyChangesWhenStoringPassword() throws KeychainAccessException, InterruptedException {
|
public void testPropertyChangesWhenStoringPassword() throws KeychainAccessException, InterruptedException {
|
||||||
@@ -43,7 +42,7 @@ public class KeychainManagerTest {
|
|||||||
ReadOnlyBooleanProperty property = keychainManager.getPassphraseStoredProperty("test");
|
ReadOnlyBooleanProperty property = keychainManager.getPassphraseStoredProperty("test");
|
||||||
Assertions.assertFalse(property.get());
|
Assertions.assertFalse(property.get());
|
||||||
|
|
||||||
keychainManager.storePassphrase("test", null,"bar");
|
keychainManager.storePassphrase("test", null, "bar");
|
||||||
|
|
||||||
AtomicBoolean result = new AtomicBoolean(false);
|
AtomicBoolean result = new AtomicBoolean(false);
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.cryptomator.ui.controls;
|
package org.cryptomator.ui.controls;
|
||||||
|
|
||||||
|
import org.cryptomator.JavaFXUtil;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Assumptions;
|
import org.junit.jupiter.api.Assumptions;
|
||||||
@@ -18,10 +19,8 @@ public class SecurePasswordFieldTest {
|
|||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void initJavaFx() throws InterruptedException {
|
public static void initJavaFx() throws InterruptedException {
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
var isRunning = JavaFXUtil.startPlatform();
|
||||||
Platform.startup(latch::countDown);
|
Assumptions.assumeTrue(isRunning);
|
||||||
var javafxStarted = latch.await(5, TimeUnit.SECONDS);
|
|
||||||
Assumptions.assumeTrue(javafxStarted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|||||||
Reference in New Issue
Block a user