mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-14 08:41:28 +00:00
Merge pull request #4179 from cryptomator/feature/admin-config-allowlist
Hub: Trust on first use
This commit is contained in:
2
.idea/runConfigurations/Cryptomator_Linux.xml
generated
2
.idea/runConfigurations/Cryptomator_Linux.xml
generated
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="true" -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Dfuse.experimental="true" -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
2
.idea/runConfigurations/Cryptomator_Windows.xml
generated
2
.idea/runConfigurations/Cryptomator_Windows.xml
generated
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.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.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,javafx.graphics" />
|
||||
<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 -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.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.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,javafx.graphics" />
|
||||
<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 -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
@@ -5,7 +5,7 @@
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator-Dev" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Library/Application Support/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator-Dev" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Library/Application Support/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -9,10 +9,13 @@ import org.slf4j.LoggerFactory;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@@ -20,20 +23,22 @@ public class Environment {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
|
||||
private static final int DEFAULT_MIN_PW_LENGTH = 8;
|
||||
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 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 LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
||||
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
||||
private static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
|
||||
private static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
|
||||
private static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
|
||||
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
||||
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
||||
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
||||
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
|
||||
public static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
||||
public static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
||||
public static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
||||
public static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
|
||||
public static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
||||
public static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
||||
public static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
||||
public static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
|
||||
public static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
|
||||
public static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
|
||||
public static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
||||
public static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
||||
public static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
||||
public static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
|
||||
public static final String HUB_ALLOWED_HOSTS_PROP_NAME = "cryptomator.hub.allowedHosts";
|
||||
public static final String HUB_TOFU_PROP_NAME = "cryptomator.hub.enableTrustOnFirstUse";
|
||||
|
||||
private Environment() {}
|
||||
|
||||
@@ -57,6 +62,8 @@ public class Environment {
|
||||
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
|
||||
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
|
||||
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
|
||||
logCryptomatorSystemProperty(HUB_ALLOWED_HOSTS_PROP_NAME);
|
||||
logCryptomatorSystemProperty(HUB_TOFU_PROP_NAME);
|
||||
}
|
||||
|
||||
public static Environment getInstance() {
|
||||
@@ -145,6 +152,18 @@ public class Environment {
|
||||
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
|
||||
}
|
||||
|
||||
public Set<String> hubAllowedHosts() {
|
||||
var allowedHubHostsString = System.getProperty(HUB_ALLOWED_HOSTS_PROP_NAME, "");
|
||||
return Arrays.stream(allowedHubHostsString.split(","))
|
||||
.map(String::trim)
|
||||
.filter(Predicate.not(String::isEmpty))
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
public boolean hubTrustOnFirstUse() {
|
||||
return Boolean.getBoolean(HUB_TOFU_PROP_NAME);
|
||||
}
|
||||
|
||||
private Optional<Path> getPath(String propertyName) {
|
||||
String value = System.getProperty(propertyName);
|
||||
return Optional.ofNullable(value).map(Paths::get);
|
||||
|
||||
@@ -24,9 +24,12 @@ import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.geometry.NodeOrientation;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Settings {
|
||||
|
||||
@@ -78,6 +81,7 @@ public class Settings {
|
||||
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
|
||||
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
|
||||
public final StringProperty lastUpdateAttemptedByVersion;
|
||||
public final ObservableSet<String> trustedHosts;
|
||||
|
||||
public static Settings create(SettingsProvider provider, Environment env) {
|
||||
var defaults = new SettingsJson();
|
||||
@@ -118,6 +122,7 @@ public class Settings {
|
||||
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
|
||||
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
|
||||
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
|
||||
this.trustedHosts = FXCollections.observableSet(json.trustedHosts);
|
||||
|
||||
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
|
||||
|
||||
@@ -149,6 +154,7 @@ public class Settings {
|
||||
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
|
||||
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
|
||||
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
|
||||
trustedHosts.addListener(this::somethingChanged);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@@ -207,6 +213,7 @@ public class Settings {
|
||||
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
|
||||
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
|
||||
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
|
||||
json.trustedHosts = Set.copyOf(trustedHosts);
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,23 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||
import com.fasterxml.jackson.annotation.Nulls;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
class SettingsJson {
|
||||
|
||||
@JsonProperty("directories")
|
||||
List<VaultSettingsJson> directories = List.of();
|
||||
@JsonSetter(nulls = Nulls.AS_EMPTY)
|
||||
List<VaultSettingsJson> directories = new ArrayList<>();
|
||||
|
||||
@JsonProperty("writtenByVersion")
|
||||
String writtenByVersion;
|
||||
@@ -99,4 +105,8 @@ class SettingsJson {
|
||||
|
||||
@JsonProperty("lastUpdateAttemptedByVersion")
|
||||
String lastUpdateAttemptedByVersion;
|
||||
|
||||
@JsonProperty("trustedHosts")
|
||||
@JsonSetter(nulls = Nulls.AS_EMPTY)
|
||||
Set<String> trustedHosts = new HashSet<>();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import java.util.Set;
|
||||
* <li>cryptomator.p12Path</li>
|
||||
* <li>cryptomator.mountPointsDir</li>
|
||||
* <li>cryptomator.disableUpdateCheck</li>
|
||||
* <li>cryptomator.hub.allowedHosts</li>
|
||||
* <li>cryptomator.hub.enableTrustOnFirstUse</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see Properties
|
||||
@@ -42,7 +44,9 @@ class AdminPropertiesFactory {
|
||||
"cryptomator.pluginDir", //
|
||||
"cryptomator.p12Path", //
|
||||
"cryptomator.mountPointsDir", //
|
||||
"cryptomator.disableUpdateCheck");
|
||||
"cryptomator.disableUpdateCheck", //
|
||||
"cryptomator.hub.allowedHosts", //
|
||||
"cryptomator.hub.enableTrustOnFirstUse");
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ public enum FxmlFile {
|
||||
HEALTH_START("/fxml/health_start.fxml"), //
|
||||
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
||||
HUB_NO_KEYCHAIN("/fxml/hub_no_keychain.fxml"), //
|
||||
HUB_CHECK_HOST_TRUST("/fxml/hub_check_host_trust.fxml"), //
|
||||
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
||||
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
|
||||
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
|
||||
@@ -29,6 +30,7 @@ public enum FxmlFile {
|
||||
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
|
||||
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
||||
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
|
||||
HUB_UNTRUSTED_HOST("/fxml/hub_untrusted_host.fxml"), //
|
||||
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
|
||||
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
||||
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.nimbusds.jose.JWEObject;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
@@ -12,8 +11,6 @@ import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.concurrent.WorkerStateEvent;
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.scene.text.TextFlow;
|
||||
import javafx.stage.Stage;
|
||||
import java.net.URI;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@KeyLoadingScoped
|
||||
public class CheckHostTrustController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CheckHostTrustController.class);
|
||||
private static final String CHECK_KEY = "hub.checkHostTrust.message.check";
|
||||
private static final String ASK_SINGULAR_KEY = "hub.checkHostTrust.message.ask";
|
||||
private static final String ASK_PLURAL_KEY = "hub.checkHostTrust.message.ask.plural";
|
||||
private static final String TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN = ".cryptomator.cloud";
|
||||
|
||||
private final Stage window;
|
||||
private final HubConfig hubConfig;
|
||||
private final URI canonicalHubUri;
|
||||
private final URI canonicalAuthUri;
|
||||
private final Lazy<Scene> authFlowScene;
|
||||
private final Lazy<Scene> untrustedHostScene;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final Settings settings;
|
||||
private final Environment env;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final SortedSet<String> hostnames;
|
||||
private final StringProperty messageLabel;
|
||||
|
||||
@FXML
|
||||
private TextFlow hostnamesFlow;
|
||||
|
||||
@Inject
|
||||
public CheckHostTrustController(@KeyLoading Stage window, //
|
||||
HubConfig hubConfig, //
|
||||
@FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, //
|
||||
@FxmlScene(FxmlFile.HUB_UNTRUSTED_HOST) Lazy<Scene> untrustedHostScene, //
|
||||
CompletableFuture<ReceivedKey> result, //
|
||||
Settings settings, //
|
||||
Environment env, //
|
||||
ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
this.hubConfig = hubConfig;
|
||||
this.canonicalHubUri = hubConfig.getApiBaseUrl();
|
||||
this.canonicalAuthUri = URI.create(hubConfig.authEndpoint);
|
||||
this.authFlowScene = authFlowScene;
|
||||
this.untrustedHostScene = untrustedHostScene;
|
||||
this.result = result;
|
||||
this.settings = settings;
|
||||
this.env = env;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.hostnames = new TreeSet<>();
|
||||
this.messageLabel = new SimpleStringProperty(resourceBundle.getString(CHECK_KEY));
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
if (!isConsistentHubConfig()) {
|
||||
LOG.warn("Inconsistent hub config detected. Denying access to protect the user.");
|
||||
deny();
|
||||
} else if (isAllCryptomatorCloud() && !isAnyHttpHost()) {
|
||||
trust(); // trust *.cryptomator.cloud by default, domain is owned by Cryptomator maintainers
|
||||
} else if (containsAllowedHosts(env.hubAllowedHosts())) {
|
||||
trust(); // trust hosts explicitly allowlisted via system property
|
||||
} else if (isAnyHttpHost() && !isAllLocalhost()) {
|
||||
LOG.warn("Denying attempt to connect to hub instance via unencrypted HTTP.");
|
||||
deny(); // never trust http hosts except for local testing
|
||||
} else if (env.hubTrustOnFirstUse() && containsAllowedHosts(settings.trustedHosts)) {
|
||||
trust(); // trust hosts previously allowlisted by the user
|
||||
} else if (env.hubTrustOnFirstUse()) {
|
||||
hostnames.add(getAuthority(canonicalHubUri));
|
||||
hostnames.add(getAuthority(canonicalAuthUri));
|
||||
renderHostnames(); // ask user whether to trust these hosts
|
||||
} else {
|
||||
LOG.warn("Cryptomator is not allowed to connect to {}. Check your {} config.", getAuthority(canonicalHubUri), Environment.HUB_ALLOWED_HOSTS_PROP_NAME);
|
||||
deny();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void trust() {
|
||||
settings.trustedHosts.addAll(hostnames);
|
||||
Platform.runLater(() -> {
|
||||
window.setScene(authFlowScene.get());
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void deny() {
|
||||
result.cancel(true);
|
||||
Platform.runLater(() -> {
|
||||
window.setScene(untrustedHostScene.get());
|
||||
});
|
||||
}
|
||||
|
||||
private void renderHostnames() {
|
||||
hostnamesFlow.getChildren().clear();
|
||||
for (var hostname : hostnames) {
|
||||
hostnamesFlow.getChildren().add(new Text(hostname + System.lineSeparator()));
|
||||
}
|
||||
var messageKey = hostnames.size() > 1 ? ASK_PLURAL_KEY : ASK_SINGULAR_KEY;
|
||||
messageLabel.set(resourceBundle.getString(messageKey));
|
||||
}
|
||||
|
||||
private boolean isConsistentHubConfig() {
|
||||
var canonicalHubAuthority = getAuthority(canonicalHubUri);
|
||||
var canonicalAuthAuthority = getAuthority(canonicalAuthUri);
|
||||
|
||||
// apiBaseURL.host == deviceUrl.host == authSuccessUrl.host == authErrorUrl.host
|
||||
return (hubConfig.apiBaseUrl == null || getAuthority(hubConfig.apiBaseUrl).equals(canonicalHubAuthority)) //
|
||||
&& (hubConfig.devicesResourceUrl == null || getAuthority(hubConfig.devicesResourceUrl).equals(canonicalHubAuthority)) //
|
||||
&& getAuthority(hubConfig.authSuccessUrl).equals(canonicalHubAuthority) //
|
||||
&& getAuthority(hubConfig.authErrorUrl).equals(canonicalHubAuthority) //
|
||||
// authUrl.host == tokenUrl.host:
|
||||
&& getAuthority(hubConfig.tokenEndpoint).equals(canonicalAuthAuthority);
|
||||
}
|
||||
|
||||
private boolean isAllCryptomatorCloud() {
|
||||
return canonicalHubUri.getHost().endsWith(TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN) && canonicalAuthUri.getHost().endsWith(TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN);
|
||||
}
|
||||
|
||||
private boolean isAnyHttpHost() {
|
||||
return "http".equalsIgnoreCase(canonicalHubUri.getScheme()) || "http".equalsIgnoreCase(canonicalAuthUri.getScheme());
|
||||
}
|
||||
|
||||
private boolean isAllLocalhost() {
|
||||
return "localhost".equalsIgnoreCase(canonicalHubUri.getHost()) && "localhost".equalsIgnoreCase(canonicalAuthUri.getHost());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean containsAllowedHosts(Set<String> allowedHubHosts) {
|
||||
return allowedHubHosts.contains(getAuthority(canonicalHubUri)) && allowedHubHosts.contains(getAuthority(canonicalAuthUri));
|
||||
}
|
||||
|
||||
public static String getAuthority(String string) {
|
||||
return getAuthority(URI.create(string));
|
||||
}
|
||||
|
||||
public static String getAuthority(URI uri) {
|
||||
return switch (uri.getPort()) {
|
||||
case -1 -> "%s://%s".formatted(uri.getScheme(), uri.getHost());
|
||||
case 80 -> "http://%s".formatted(uri.getHost());
|
||||
case 443 -> "https://%s".formatted(uri.getHost());
|
||||
default -> "%s://%s:%s".formatted(uri.getScheme(), uri.getHost(), uri.getPort());
|
||||
};
|
||||
}
|
||||
|
||||
//--- JavaFX property getter & setter
|
||||
public StringProperty messageLabelProperty() {
|
||||
return messageLabel;
|
||||
}
|
||||
|
||||
public String getMessageLabel() {
|
||||
return messageLabel.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -98,6 +98,13 @@ public abstract class HubKeyLoadingModule {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_NO_KEYCHAIN);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_CHECK_HOST_TRUST)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideHubCheckHostTrustScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_CHECK_HOST_TRUST);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_AUTH_FLOW)
|
||||
@KeyLoadingScoped
|
||||
@@ -168,6 +175,13 @@ public abstract class HubKeyLoadingModule {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_UNTRUSTED_HOST)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideHubUntrustedHostScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_UNTRUSTED_HOST);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
|
||||
@KeyLoadingScoped
|
||||
@@ -180,6 +194,11 @@ public abstract class HubKeyLoadingModule {
|
||||
@FxControllerKey(NoKeychainController.class)
|
||||
abstract FxController bindNoKeychainController(NoKeychainController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(CheckHostTrustController.class)
|
||||
abstract FxController bindCheckHostAuthenticityController(CheckHostTrustController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(AuthFlowController.class)
|
||||
@@ -225,6 +244,11 @@ public abstract class HubKeyLoadingModule {
|
||||
@FxControllerKey(UnauthorizedDeviceController.class)
|
||||
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(UntrustedHostController.class)
|
||||
abstract FxController bindUnauthorizedHostController(UntrustedHostController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(RequireAccountInitController.class)
|
||||
|
||||
@@ -36,19 +36,19 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
|
||||
private final Stage window;
|
||||
private final KeychainManager keychainManager;
|
||||
private final AtomicReference<String> fsOwnerId;
|
||||
private final Lazy<Scene> authFlowScene;
|
||||
private final Lazy<Scene> checkHostTrustScene;
|
||||
private final Lazy<Scene> noKeychainScene;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final DeviceKey deviceKey;
|
||||
|
||||
@Inject
|
||||
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
|
||||
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_CHECK_HOST_TRUST) Lazy<Scene> checkHostTrustScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
|
||||
this.window = window;
|
||||
this.keychainManager = keychainManager;
|
||||
this.fsOwnerId = fsOwnerId;
|
||||
window.setTitle(windowTitle);
|
||||
window.setOnCloseRequest(_ -> result.cancel(true));
|
||||
this.authFlowScene = authFlowScene;
|
||||
this.checkHostTrustScene = checkHostTrustScene;
|
||||
this.noKeychainScene = noKeychainScene;
|
||||
this.result = result;
|
||||
this.deviceKey = deviceKey;
|
||||
@@ -62,7 +62,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
|
||||
throw new NoKeychainAccessProviderException();
|
||||
}
|
||||
var keypair = deviceKey.get();
|
||||
showWindow(authFlowScene);
|
||||
showWindow(checkHostTrustScene);
|
||||
var jwe = result.get();
|
||||
return jwe.decryptMasterkey(keypair.getPrivate());
|
||||
} catch (NoKeychainAccessProviderException e) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@KeyLoadingScoped
|
||||
public class UntrustedHostController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
|
||||
@Inject
|
||||
public UntrustedHostController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
|
||||
this.window = window;
|
||||
this.result = result;
|
||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
private void windowClosed(WindowEvent windowEvent) {
|
||||
result.cancel(true);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import javax.inject.Inject;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
@@ -56,6 +57,7 @@ public class GeneralPreferencesController implements FxController {
|
||||
public CheckBox autoCloseVaultsCheckbox;
|
||||
public CheckBox debugModeCheckbox;
|
||||
public CheckBox autoStartCheckbox;
|
||||
public Button resetTrustedHostsButton;
|
||||
public ToggleGroup nodeOrientation;
|
||||
|
||||
private CompletionStage<Void> keychainMigrations = CompletableFuture.completedFuture(null);
|
||||
@@ -105,6 +107,9 @@ public class GeneralPreferencesController implements FxController {
|
||||
quickAccessServiceChoiceBox.setConverter(new NamedServiceConverter<>());
|
||||
Bindings.bindBidirectional(settings.quickAccessService, quickAccessServiceChoiceBox.valueProperty(), quickAccessSettingsConverter);
|
||||
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
|
||||
if (resetTrustedHostsButton != null) {
|
||||
resetTrustedHostsButton.disableProperty().bind(Bindings.isEmpty(settings.trustedHosts));
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
|
||||
@@ -131,6 +136,10 @@ public class GeneralPreferencesController implements FxController {
|
||||
return autoStartProvider.isPresent();
|
||||
}
|
||||
|
||||
public boolean isHubTrustOnFirstUseEnabled() {
|
||||
return environment.hubTrustOnFirstUse();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void toggleAutoStart() {
|
||||
autoStartProvider.ifPresent(autoStart -> {
|
||||
@@ -153,6 +162,11 @@ public class GeneralPreferencesController implements FxController {
|
||||
return !quickAccessServices.isEmpty();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void resetTrustedHosts() {
|
||||
settings.trustedHosts.clear();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void showLogfileDirectory() {
|
||||
try {
|
||||
|
||||
53
src/main/resources/fxml/hub_check_host_trust.fxml
Normal file
53
src/main/resources/fxml/hub_check_host_trust.fxml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<?import javafx.scene.text.TextFlow?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.CheckHostTrustController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT"
|
||||
accessibleRole="DIALOG">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="QUESTION" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="${controller.messageLabel}" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<TextFlow fx:id="hostnamesFlow" styleClass="text-flow" minHeight="60"/>
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
|
||||
<buttons>
|
||||
<Button text="%hub.checkHostTrust.denyBtn" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#deny"/>
|
||||
<Button text="%hub.checkHostTrust.trustBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#trust"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
||||
52
src/main/resources/fxml/hub_untrusted_host.fxml
Normal file
52
src/main/resources/fxml/hub_untrusted_host.fxml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.UntrustedHostController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT"
|
||||
accessibleRole="DIALOG">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="%hub.untrustedHost.message" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%hub.untrustedHost.description" wrapText="true"/>
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
|
||||
<buttons>
|
||||
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" onAction="#close"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.control.ChoiceBox?>
|
||||
<?import javafx.scene.control.Hyperlink?>
|
||||
@@ -34,6 +35,8 @@
|
||||
<CheckBox fx:id="useQuickAccessCheckbox" text="%preferences.general.quickAccessService"/>
|
||||
<ChoiceBox fx:id="quickAccessServiceChoiceBox" accessibleText="%preferences.general.quickAccessService"/>
|
||||
</HBox>
|
||||
|
||||
<Button fx:id="resetTrustedHostsButton" text="%preferences.general.resetTrustedHosts" visible="${controller.hubTrustOnFirstUseEnabled}" managed="${controller.hubTrustOnFirstUseEnabled}" onAction="#resetTrustedHosts"/>
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
|
||||
@@ -162,6 +162,12 @@ unlock.error.title=Unlock "%s" failed
|
||||
hub.noKeychain.message=Unable to access device key
|
||||
hub.noKeychain.description=In order to unlock Hub vaults, a device key is required, which is secured using a keychain. To proceed, enable “%s” and select a keychain in the preferences.
|
||||
hub.noKeychain.openBtn=Open Preferences
|
||||
### Check Host Authenticity
|
||||
hub.checkHostTrust.message.check=Checking Configuration…
|
||||
hub.checkHostTrust.message.ask=Trust this host?
|
||||
hub.checkHostTrust.message.ask.plural=Trust these hosts?
|
||||
hub.checkHostTrust.trustBtn=Trust
|
||||
hub.checkHostTrust.denyBtn=Deny
|
||||
### Waiting
|
||||
hub.auth.message=Waiting for authentication…
|
||||
hub.auth.description=You should automatically be redirected to the login page.
|
||||
@@ -193,6 +199,9 @@ hub.archived.description=This vault has been archived and is no longer accessibl
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Access denied
|
||||
hub.unauthorized.description=You are not authorized to open this vault. Contact the vault's owner to request access.
|
||||
### Untrusted Host
|
||||
hub.untrustedHost.message=Host not trusted
|
||||
hub.untrustedHost.description=Connection to Hub was blocked for your security. If you believe the Hub host is safe, contact your Hub administrator or try again.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Action required
|
||||
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
|
||||
@@ -306,6 +315,7 @@ preferences.general.debugDirectory=Reveal log files
|
||||
preferences.general.autoStart=Launch Cryptomator on system start
|
||||
preferences.general.keychainBackend=Store passwords with
|
||||
preferences.general.quickAccessService=Add unlocked vaults to the quick access area
|
||||
preferences.general.resetTrustedHosts=Reset trusted hosts
|
||||
## Interface
|
||||
preferences.interface=Interface
|
||||
preferences.interface.theme=Look & Feel
|
||||
@@ -717,4 +727,4 @@ eventView.entry.inUse.ignoreLock=Ignore use status
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=File is in use on another device
|
||||
notification.inUse.description=The file is open by %s on %s. Ask them to close the file and let synchronization finish. You can ignore the status to open it now, but this may cause conflicts or overwrite newer changes.
|
||||
notification.inUse.action=Ignore Use Status
|
||||
notification.inUse.action=Ignore Use Status
|
||||
|
||||
@@ -29,7 +29,8 @@ public class SettingsJsonTest {
|
||||
"checkForUpdatesEnabled": true,
|
||||
"port": 8080,
|
||||
"language": "de-DE",
|
||||
"numTrayNotifications": 42
|
||||
"numTrayNotifications": 42,
|
||||
"trustedHosts": null
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -44,6 +45,7 @@ public class SettingsJsonTest {
|
||||
Assertions.assertTrue(jsonObj.autoCloseVaults);
|
||||
Assertions.assertEquals("de-DE", jsonObj.language);
|
||||
Assertions.assertEquals(42, jsonObj.numTrayNotifications);
|
||||
Assertions.assertEquals(0, jsonObj.trustedHosts.size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
class CheckHostTrustControllerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"https://auth.example.com, https://hub.example.com, true",
|
||||
"https://hub.example.com, https://hub.example.com, true",
|
||||
"https://auth.example.com, https://auth.example.com, true",
|
||||
"https://auth.example.com, https://wrong.example.com, false",
|
||||
"https://wrong.example.com, https://wrong.example.com, false"
|
||||
})
|
||||
void testContainsAllowedHosts(String apiBase, String authEndpoint, boolean expectedResult) {
|
||||
var hubConfig = new HubConfig();
|
||||
hubConfig.apiBaseUrl = apiBase;
|
||||
hubConfig.authEndpoint = authEndpoint;
|
||||
var controller = new CheckHostTrustController(Mockito.mock(), hubConfig, Mockito.mock(), Mockito.mock(), Mockito.mock(), Mockito.mock(), Mockito.mock(), Mockito.mock());
|
||||
|
||||
var actualResult = controller.containsAllowedHosts(Set.of("https://auth.example.com", "https://hub.example.com"));
|
||||
|
||||
Assertions.assertEquals(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"https://example.com, https://example.com",
|
||||
"https://example.com/foo/bar, https://example.com",
|
||||
"https://example.com:8080, https://example.com:8080",
|
||||
"https://user@example.com:8080/foo/bar, https://example.com:8080",
|
||||
"https://user@example.com:443/foo/bar, https://example.com",
|
||||
"http://user@example.com:80/foo/bar?foo=bar, http://example.com",
|
||||
"http://user@example.com:8080/foo/bar?foo=bar, http://example.com:8080"
|
||||
})
|
||||
void testGetAuthority(String input, String expected) {
|
||||
var actual = CheckHostTrustController.getAuthority(input);
|
||||
|
||||
Assertions.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user