diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8009aeb06..b847d0b4b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,6 @@ jobs: build: name: Build and Test runs-on: ubuntu-latest - #This check is case insensitive if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')" steps: - uses: actions/checkout@v2 @@ -29,28 +28,31 @@ jobs: run: | curl -o ~/codacy-coverage-reporter.jar https://repo.maven.apache.org/maven2/com/codacy/codacy-coverage-reporter/7.1.0/codacy-coverage-reporter-7.1.0-assembly.jar $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/commons/target/site/jacoco/jacoco.xml --partial - $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/keychain/target/site/jacoco/jacoco.xml --partial $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/ui/target/site/jacoco/jacoco.xml --partial $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/launcher/target/site/jacoco/jacoco.xml --partial $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar final env: CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} - - name: Assemble Buildkit - run: mvn -B package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease + - name: Assemble buildkit-linux.zip + run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,linux - name: Upload buildkit-linux.zip uses: actions/upload-artifact@v1 with: - name: buildkit-linux.zip + name: buildkit-linux path: main/buildkit/target/buildkit-linux.zip + - name: Assemble buildkit-mac.zip + run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,mac - name: Upload buildkit-mac.zip uses: actions/upload-artifact@v1 with: - name: buildkit-mac.zip + name: buildkit-mac path: main/buildkit/target/buildkit-mac.zip + - name: Assemble buildkit-win.zip + run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,windows - name: Upload buildkit-win.zip uses: actions/upload-artifact@v1 with: - name: buildkit-win.zip + name: buildkit-win path: main/buildkit/target/buildkit-win.zip release: diff --git a/main/buildkit/assembly-linux.xml b/main/buildkit/assembly-linux.xml index 5d8bfc785..0764ada20 100644 --- a/main/buildkit/assembly-linux.xml +++ b/main/buildkit/assembly-linux.xml @@ -43,12 +43,5 @@ libs - - target/linux-libs - - *.jar - - libs - \ No newline at end of file diff --git a/main/buildkit/assembly-mac.xml b/main/buildkit/assembly-mac.xml index c9057dcdd..c7a10f2ef 100644 --- a/main/buildkit/assembly-mac.xml +++ b/main/buildkit/assembly-mac.xml @@ -43,12 +43,5 @@ libs - - target/mac-libs - - *.jar - - libs - \ No newline at end of file diff --git a/main/buildkit/assembly-win.xml b/main/buildkit/assembly-win.xml index 2e81782b3..e0ff7df4b 100644 --- a/main/buildkit/assembly-win.xml +++ b/main/buildkit/assembly-win.xml @@ -43,12 +43,5 @@ libs - - target/win-libs - - *.jar - - libs - \ No newline at end of file diff --git a/main/buildkit/pom.xml b/main/buildkit/pom.xml index 23059eb4a..4dde902b9 100644 --- a/main/buildkit/pom.xml +++ b/main/buildkit/pom.xml @@ -24,7 +24,6 @@ org.apache.maven.plugins maven-resources-plugin - 3.1.0 copy-resources @@ -55,8 +54,8 @@ + org.apache.maven.plugins maven-dependency-plugin - 3.1.1 copy-libs @@ -65,110 +64,153 @@ copy-dependencies + runtime ${project.build.directory}/libs linux,mac,win - dbus-java,secret-service,kdewallet,hkdf,java-utils - - - - copy-linux-libs - prepare-package - - copy-dependencies - - - ${project.build.directory}/linux-libs - org.openjfx - linux - - - - copy-linux-system-keychain-access - prepare-package - - copy-dependencies - - - ${project.build.directory}/linux-libs - dbus-java,secret-service,kdewallet,hkdf,java-utils - - - - copy-mac-libs - prepare-package - - copy-dependencies - - - ${project.build.directory}/mac-libs - org.openjfx - mac - - - - copy-win-libs - prepare-package - - copy-dependencies - - - ${project.build.directory}/win-libs - org.openjfx - win - - - - - - - - maven-assembly-plugin - 3.1.1 - - - assemble-linux - package - - single - - - - assembly-linux.xml - - false - buildkit-linux - - - - assemble-mac - package - - single - - - - assembly-mac.xml - - false - buildkit-mac - - - - assemble-win - package - - single - - - - assembly-win.xml - - false - buildkit-win + + + + linux + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble-linux + package + + single + + + + assembly-linux.xml + + false + buildkit-linux + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-linux-libs + prepare-package + + copy-dependencies + + + ${project.build.directory}/libs + org.openjfx + linux + + + + + + + + + + mac + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble-mac + package + + single + + + + assembly-mac.xml + + false + buildkit-mac + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-mac-libs + prepare-package + + copy-dependencies + + + ${project.build.directory}/libs + org.openjfx + mac + + + + + + + + + + windows + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble-win + package + + single + + + + assembly-win.xml + + false + buildkit-win + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-win-libs + prepare-package + + copy-dependencies + + + ${project.build.directory}/libs + org.openjfx + win + + + + + + + + \ No newline at end of file diff --git a/main/commons/pom.xml b/main/commons/pom.xml index 1927a4889..7eaccf881 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -27,6 +27,10 @@ org.cryptomator webdav-nio-adapter + + org.cryptomator + integrations-api + org.cryptomator jni diff --git a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java b/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java index 5ace6a3e2..ed278e1ab 100644 --- a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java @@ -9,6 +9,7 @@ import com.tobiasdiez.easybind.EasyBind; import dagger.Module; import dagger.Provides; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.keychain.KeychainModule; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.SettingsProvider; import org.cryptomator.common.vaults.Vault; @@ -33,7 +34,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -@Module(subcomponents = {VaultComponent.class}) +@Module(subcomponents = {VaultComponent.class}, includes = {KeychainModule.class}) public abstract class CommonsModule { private static final Logger LOG = LoggerFactory.getLogger(CommonsModule.class); diff --git a/main/commons/src/main/java/org/cryptomator/common/JniModule.java b/main/commons/src/main/java/org/cryptomator/common/JniModule.java index 180736710..aaeda2964 100644 --- a/main/commons/src/main/java/org/cryptomator/common/JniModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/JniModule.java @@ -15,6 +15,7 @@ import javax.inject.Singleton; import java.util.Optional; @Module +@Deprecated public class JniModule { @Provides diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainManager.java b/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java similarity index 70% rename from main/keychain/src/main/java/org/cryptomator/keychain/KeychainManager.java rename to main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java index 515195232..537b83577 100644 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainManager.java +++ b/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java @@ -1,60 +1,71 @@ -package org.cryptomator.keychain; +package org.cryptomator.common.keychain; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.cryptomator.integrations.keychain.KeychainAccessException; +import org.cryptomator.integrations.keychain.KeychainAccessProvider; +import javax.inject.Inject; +import javax.inject.Singleton; import javafx.application.Platform; +import javafx.beans.binding.ObjectExpression; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import java.util.Arrays; -public class KeychainManager implements KeychainAccessStrategy { +@Singleton +public class KeychainManager implements KeychainAccessProvider { - private static final Logger LOG = LoggerFactory.getLogger(KeychainManager.class); + private final ObjectExpression keychain; + private final LoadingCache passphraseStoredProperties; - private final KeychainAccessStrategy keychain; - private LoadingCache passphraseStoredProperties; - - KeychainManager(KeychainAccessStrategy keychain) { - assert keychain.isSupported(); - this.keychain = keychain; + @Inject + KeychainManager(ObjectExpression selectedKeychain) { + this.keychain = selectedKeychain; this.passphraseStoredProperties = CacheBuilder.newBuilder() // .weakValues() // .build(CacheLoader.from(this::createStoredPassphraseProperty)); + keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll()); + } + + private KeychainAccessProvider getKeychainOrFail() throws KeychainAccessException { + var result = keychain.getValue(); + if (result == null) { + throw new NoKeychainAccessProviderException(); + } + return result; } @Override public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - keychain.storePassphrase(key, passphrase); + getKeychainOrFail().storePassphrase(key, passphrase); setPassphraseStored(key, true); } @Override public char[] loadPassphrase(String key) throws KeychainAccessException { - char[] passphrase = keychain.loadPassphrase(key); + char[] passphrase = getKeychainOrFail().loadPassphrase(key); setPassphraseStored(key, passphrase != null); return passphrase; } @Override public void deletePassphrase(String key) throws KeychainAccessException { - keychain.deletePassphrase(key); + getKeychainOrFail().deletePassphrase(key); setPassphraseStored(key, false); } @Override public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - keychain.changePassphrase(key, passphrase); + getKeychainOrFail().changePassphrase(key, passphrase); setPassphraseStored(key, true); } @Override public boolean isSupported() { - return true; + return keychain.getValue() != null; } /** @@ -69,7 +80,7 @@ public class KeychainManager implements KeychainAccessStrategy { public boolean isPassphraseStored(String key) throws KeychainAccessException { char[] storedPw = null; try { - storedPw = keychain.loadPassphrase(key); + storedPw = getKeychainOrFail().loadPassphrase(key); return storedPw != null; } finally { if (storedPw != null) { @@ -84,7 +95,6 @@ public class KeychainManager implements KeychainAccessStrategy { if (Platform.isFxApplicationThread()) { property.set(value); } else { - LOG.warn(""); Platform.runLater(() -> property.set(value)); } } @@ -107,7 +117,6 @@ public class KeychainManager implements KeychainAccessStrategy { private BooleanProperty createStoredPassphraseProperty(String key) { try { - LOG.warn("LOAD"); // TODO remove return new SimpleBooleanProperty(isPassphraseStored(key)); } catch (KeychainAccessException e) { return new SimpleBooleanProperty(false); diff --git a/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java b/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java new file mode 100644 index 000000000..9ac343d36 --- /dev/null +++ b/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java @@ -0,0 +1,44 @@ +package org.cryptomator.common.keychain; + +import dagger.Module; +import dagger.Provides; +import org.cryptomator.common.settings.Settings; +import org.cryptomator.integrations.keychain.KeychainAccessProvider; + +import javax.inject.Singleton; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.ObjectExpression; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.stream.Collectors; + +@Module +public class KeychainModule { + + @Provides + @Singleton + static Set> provideAvailableKeychainAccessProviderFactories() { + return ServiceLoader.load(KeychainAccessProvider.class).stream().collect(Collectors.toUnmodifiableSet()); + } + + @Provides + @Singleton + static Set provideSupportedKeychainAccessProviders(Set> availableFactories) { + return availableFactories.stream() // + .map(ServiceLoader.Provider::get) // + .filter(KeychainAccessProvider::isSupported) // + .collect(Collectors.toUnmodifiableSet()); + } + + @Provides + @Singleton + static ObjectExpression provideKeychainAccessProvider(Settings settings, Set providers) { + return Bindings.createObjectBinding(() -> { + var selectedProviderClass = settings.keychainBackend().get().getProviderClass(); + var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny(); + var fallbackProvider = providers.stream().findAny().orElse(null); + return selectedProvider.orElse(fallbackProvider); + }, settings.keychainBackend()); + } + +} diff --git a/main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java b/main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java new file mode 100644 index 000000000..c14b81076 --- /dev/null +++ b/main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java @@ -0,0 +1,13 @@ +package org.cryptomator.common.keychain; + +import org.cryptomator.integrations.keychain.KeychainAccessException; + +/** + * Thrown by {@link KeychainManager} if attempted to access a keychain despite no supported keychain access provider being available. + */ +public class NoKeychainAccessProviderException extends KeychainAccessException { + + public NoKeychainAccessProviderException() { + super("Did not find any supported keychain access provider."); + } +} diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/KeychainBackend.java b/main/commons/src/main/java/org/cryptomator/common/settings/KeychainBackend.java index a54c81b08..65f869a12 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/KeychainBackend.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/KeychainBackend.java @@ -1,39 +1,19 @@ package org.cryptomator.common.settings; -import org.apache.commons.lang3.SystemUtils; - -import java.util.Arrays; - public enum KeychainBackend { - GNOME("preferences.general.keychainBackend.gnome", SystemUtils.IS_OS_LINUX), // - KDE("preferences.general.keychainBackend.kde", SystemUtils.IS_OS_LINUX), // - MAC_SYSTEM_KEYCHAIN("preferences.general.keychainBackend.macSystemKeychain", SystemUtils.IS_OS_MAC), // - WIN_SYSTEM_KEYCHAIN("preferences.general.keychainBackend.winSystemKeychain", SystemUtils.IS_OS_WINDOWS); + GNOME("org.cryptomator.linux.keychain.SecretServiceKeychainAccess"), + KDE("org.cryptomator.linux.keychain.KDEWalletKeychainAccess"), + MAC_SYSTEM_KEYCHAIN("org.cryptomator.macos.keychain.MacSystemKeychainAccess"), + WIN_SYSTEM_KEYCHAIN("org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess"); - public static KeychainBackend[] supportedBackends() { - return Arrays.stream(values()).filter(KeychainBackend::isSupported).toArray(KeychainBackend[]::new); + private final String providerClass; + + KeychainBackend(String providerClass) { + this.providerClass = providerClass; } - public static KeychainBackend defaultBackend() { - if (SystemUtils.IS_OS_LINUX) { - return KeychainBackend.GNOME; - } else { // SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS - return Arrays.stream(KeychainBackend.supportedBackends()).findFirst().orElseThrow(IllegalStateException::new); - } + public String getProviderClass() { + return providerClass; } - private final String configName; - private final boolean isSupported; - - KeychainBackend(String configName, boolean isSupported) { - this.configName = configName; - this.isSupported = isSupported; - } - - public String getDisplayName() { - return configName; - } - - public boolean isSupported() { return isSupported; } - } diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java b/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java index 22b42628d..e50391d2d 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java @@ -8,6 +8,8 @@ ******************************************************************************/ package org.cryptomator.common.settings; +import org.apache.commons.lang3.SystemUtils; + import javafx.beans.Observable; import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; @@ -33,9 +35,9 @@ public class Settings { public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3; public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV; public static final boolean DEFAULT_DEBUG_MODE = false; - public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = System.getProperty("os.name").toLowerCase().contains("windows") ? VolumeImpl.DOKANY : VolumeImpl.FUSE; + public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = SystemUtils.IS_OS_WINDOWS ? VolumeImpl.DOKANY : VolumeImpl.FUSE; public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT; - public static final KeychainBackend DEFAULT_KEYCHAIN_BACKEND = KeychainBackend.defaultBackend(); + public static final KeychainBackend DEFAULT_KEYCHAIN_BACKEND = SystemUtils.IS_OS_WINDOWS ? KeychainBackend.WIN_SYSTEM_KEYCHAIN : SystemUtils.IS_OS_MAC ? KeychainBackend.MAC_SYSTEM_KEYCHAIN : KeychainBackend.GNOME; public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT; private static final String DEFAULT_LICENSE_KEY = ""; diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainManagerTest.java b/main/commons/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java similarity index 76% rename from main/keychain/src/test/java/org/cryptomator/keychain/KeychainManagerTest.java rename to main/commons/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java index 8577b0977..e82e67e2d 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainManagerTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java @@ -1,23 +1,26 @@ -package org.cryptomator.keychain; +package org.cryptomator.common.keychain; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import javafx.application.Platform; import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -class KeychainManagerTest { +public class KeychainManagerTest { @Test public void testStoreAndLoad() throws KeychainAccessException { - KeychainManager keychainManager = new KeychainManager(new MapKeychainAccess()); + KeychainManager keychainManager = new KeychainManager(new SimpleObjectProperty<>(new MapKeychainAccess())); keychainManager.storePassphrase("test", "asd"); Assertions.assertArrayEquals("asd".toCharArray(), keychainManager.loadPassphrase("test")); } @@ -34,7 +37,7 @@ class KeychainManagerTest { @Test public void testPropertyChangesWhenStoringPassword() throws KeychainAccessException, InterruptedException { - KeychainManager keychainManager = new KeychainManager(new MapKeychainAccess()); + KeychainManager keychainManager = new KeychainManager(new SimpleObjectProperty<>(new MapKeychainAccess())); ReadOnlyBooleanProperty property = keychainManager.getPassphraseStoredProperty("test"); Assertions.assertEquals(false, property.get()); diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java b/main/commons/src/test/java/org/cryptomator/common/keychain/MapKeychainAccess.java similarity index 86% rename from main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java rename to main/commons/src/test/java/org/cryptomator/common/keychain/MapKeychainAccess.java index 26b301377..c571ad716 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java +++ b/main/commons/src/test/java/org/cryptomator/common/keychain/MapKeychainAccess.java @@ -3,12 +3,14 @@ * All rights reserved. This program and the accompanying materials * are made available under the terms of the accompanying LICENSE file. *******************************************************************************/ -package org.cryptomator.keychain; +package org.cryptomator.common.keychain; + +import org.cryptomator.integrations.keychain.KeychainAccessProvider; import java.util.HashMap; import java.util.Map; -class MapKeychainAccess implements KeychainAccessStrategy { +class MapKeychainAccess implements KeychainAccessProvider { private final Map map = new HashMap<>(); diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml deleted file mode 100644 index c70d7cacb..000000000 --- a/main/keychain/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - 4.0.0 - - org.cryptomator - main - 1.6.0-SNAPSHOT - - keychain - System Keychain Access - - - - org.cryptomator - commons - - - - - org.openjfx - javafx-base - - - org.openjfx - javafx-graphics - - - - - org.apache.commons - commons-lang3 - - - - - com.google.code.gson - gson - - - com.google.guava - guava - - - - - com.google.dagger - dagger - - - - - de.swiesend - secret-service - - - - - org.purejava - kdewallet - - - - - org.slf4j - slf4j-simple - test - - - \ No newline at end of file diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessException.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessException.java deleted file mode 100644 index b4f0cf5c8..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessException.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.cryptomator.keychain; - -/** - * Indicates an error during communication with the operating system's keychain. - */ -public class KeychainAccessException extends Exception { - - KeychainAccessException(Throwable cause) { - super(cause); - } - -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java deleted file mode 100644 index 553ce4eab..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -public interface KeychainAccessStrategy { - - /** - * Associates a passphrase with a given key. - * - * @param key Key used to retrieve the passphrase via {@link #loadPassphrase(String)}. - * @param passphrase The secret to store in this keychain. - */ - void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException; - - /** - * @param key Unique key previously used while {@link #storePassphrase(String, CharSequence) storing a passphrase}. - * @return The stored passphrase for the given key or null if no value for the given key could be found. - */ - char[] loadPassphrase(String key) throws KeychainAccessException; - - /** - * Deletes a passphrase with a given key. - * - * @param key Unique key previously used while {@link #storePassphrase(String, CharSequence) storing a passphrase}. - */ - void deletePassphrase(String key) throws KeychainAccessException; - - /** - * Updates a passphrase with a given key. Noop, if there is no item for the given key. - * - * @param key Unique key previously used while {@link #storePassphrase(String, CharSequence) storing a passphrase}. - * @param passphrase The secret to be updated in this keychain. - */ - void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException; - - /** - * @return true if this KeychainAccessStrategy works on the current machine. - * @implNote This method must not throw any exceptions and should fail fast - * returning false if it can't determine availability of the checked strategy - */ - boolean isSupported(); - -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java deleted file mode 100644 index 791eb1dd0..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import dagger.multibindings.IntoSet; -import org.cryptomator.common.JniModule; - -import javax.inject.Singleton; -import java.util.Optional; -import java.util.Set; - -@Module(includes = {JniModule.class}) -public abstract class KeychainModule { - - @Binds - @IntoSet - abstract KeychainAccessStrategy bindMacSystemKeychainAccess(MacSystemKeychainAccess keychainAccessStrategy); - - @Binds - @IntoSet - abstract KeychainAccessStrategy bindWindowsProtectedKeychainAccess(WindowsProtectedKeychainAccess keychainAccessStrategy); - - @Binds - @IntoSet - abstract KeychainAccessStrategy bindLinuxSystemKeychainAccess(LinuxSystemKeychainAccess keychainAccessStrategy); - - @Provides - @Singleton - static Optional provideSupportedKeychain(Set keychainAccessStrategies) { - return keychainAccessStrategies.stream().filter(KeychainAccessStrategy::isSupported).findFirst(); - } - - @Provides - @Singleton - public static Optional provideKeychainManager(Optional keychainAccess) { - return keychainAccess.map(KeychainManager::new); - } - -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java deleted file mode 100644 index 1f74bd18d..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.cryptomator.keychain; - -import org.freedesktop.dbus.connections.impl.DBusConnection; -import org.freedesktop.dbus.exceptions.DBusException; -import org.kde.KWallet; -import org.kde.Static; -import org.purejava.KDEWallet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class LinuxKDEWalletKeychainAccessImpl implements KeychainAccessStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(LinuxKDEWalletKeychainAccessImpl.class); - - private final String FOLDER_NAME = "Cryptomator"; - private final String APP_NAME = "Cryptomator"; - private DBusConnection connection; - private KDEWallet wallet; - private int handle = -1; - - public LinuxKDEWalletKeychainAccessImpl() throws KeychainAccessException { - try { - connection = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION); - } catch (DBusException e) { - LOG.error("Connecting to D-Bus failed:", e); - throw new KeychainAccessException(e); - } - } - - @Override - public boolean isSupported() { - try { - wallet = new KDEWallet(connection); - return wallet.isEnabled(); - } catch (Exception e) { - LOG.error("A KDEWallet could not be created:", e); - return false; - } - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - try { - if (walletIsOpen() && // - !(wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) // - && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1) // - && wallet.writePassword(handle, FOLDER_NAME, key, passphrase.toString(), APP_NAME) == 0) { - LOG.debug("Passphrase successfully stored."); - } else { - LOG.debug("Passphrase was not stored."); - } - } catch (Exception e) { - LOG.error("Storing the passphrase failed:", e); - throw new KeychainAccessException(e); - } - } - - @Override - public char[] loadPassphrase(String key) throws KeychainAccessException { - String password = ""; - try { - if (walletIsOpen()) { - password = wallet.readPassword(handle, FOLDER_NAME, key, APP_NAME); - LOG.debug("loadPassphrase: wallet is open."); - } else { - LOG.debug("loadPassphrase: wallet is closed."); - } - return (password.equals("")) ? null : password.toCharArray(); - } catch (Exception e) { - LOG.error("Loading the passphrase failed:", e); - throw new KeychainAccessException(e); - } - } - - @Override - public void deletePassphrase(String key) throws KeychainAccessException { - try { - if (walletIsOpen() // - && wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) // - && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1 // - && wallet.removeEntry(handle, FOLDER_NAME, key, APP_NAME) == 0) { - LOG.debug("Passphrase successfully deleted."); - } else { - LOG.debug("Passphrase was not deleted."); - } - } catch (Exception e) { - LOG.error("Deleting the passphrase failed:", e); - throw new KeychainAccessException(e); - } - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - try { - if (walletIsOpen() // - && wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) // - && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1 // - && wallet.writePassword(handle, FOLDER_NAME, key, passphrase.toString(), APP_NAME) == 0) { - LOG.debug("Passphrase successfully changed."); - } else { - LOG.debug("Passphrase could not be changed."); - } - } catch (Exception e) { - LOG.error("Changing the passphrase failed:", e); - throw new KeychainAccessException(e); - } - } - - private boolean walletIsOpen() throws KeychainAccessException { - try { - if (wallet.isOpen(Static.DEFAULT_WALLET)) { - // This is needed due to KeechainManager loading the passphase directly - if (handle == -1) handle = wallet.open(Static.DEFAULT_WALLET, 0, APP_NAME); - return true; - } - wallet.openAsync(Static.DEFAULT_WALLET, 0, APP_NAME, false); - wallet.getSignalHandler().await(KWallet.walletAsyncOpened.class, Static.ObjectPaths.KWALLETD5, () -> null); - handle = wallet.getSignalHandler().getLastHandledSignal(KWallet.walletAsyncOpened.class, Static.ObjectPaths.KWALLETD5).handle; - LOG.debug("Wallet successfully initialized."); - return handle != -1; - } catch (Exception e) { - LOG.error("Asynchronous opening the wallet failed:", e); - throw new KeychainAccessException(e); - } - } -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccessImpl.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccessImpl.java deleted file mode 100644 index e422fff2e..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccessImpl.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.cryptomator.keychain; - -import org.freedesktop.secret.simple.SimpleCollection; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -class LinuxSecretServiceKeychainAccessImpl implements KeychainAccessStrategy { - - private final String LABEL_FOR_SECRET_IN_KEYRING = "Cryptomator"; - - @Override - public boolean isSupported() { - try (@SuppressWarnings("unused") SimpleCollection keyring = new SimpleCollection()) { - // seems like we're able to access the keyring. - return true; - } catch (IOException | RuntimeException e) { - return false; - } - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - try (SimpleCollection keyring = new SimpleCollection()) { - List list = keyring.getItems(createAttributes(key)); - if (list == null) { - keyring.createItem(LABEL_FOR_SECRET_IN_KEYRING, passphrase, createAttributes(key)); - } else { - changePassphrase(key, passphrase); - } - } catch (IOException e) { - throw new KeychainAccessException(e); - } - } - - @Override - public char[] loadPassphrase(String key) throws KeychainAccessException { - try (SimpleCollection keyring = new SimpleCollection()) { - List list = keyring.getItems(createAttributes(key)); - if (list != null) { - return keyring.getSecret(list.get(0)); - } else { - return null; - } - } catch (IOException e) { - throw new KeychainAccessException(e); - } - } - - @Override - public void deletePassphrase(String key) throws KeychainAccessException { - try (SimpleCollection keyring = new SimpleCollection()) { - List list = keyring.getItems(createAttributes(key)); - if (list != null) { - keyring.deleteItem(list.get(0)); - } - } catch (IOException e) { - throw new KeychainAccessException(e); - } - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - try (SimpleCollection keyring = new SimpleCollection()) { - List list = keyring.getItems(createAttributes(key)); - if (list != null) { - keyring.updateItem(list.get(0), LABEL_FOR_SECRET_IN_KEYRING, passphrase, createAttributes(key)); - } - } catch (IOException e) { - throw new KeychainAccessException(e); - } - } - - private Map createAttributes(String key) { - Map attributes = new HashMap(); - attributes.put("Vault", key); - return attributes; - } -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java deleted file mode 100644 index f49ea5d1d..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.cryptomator.keychain; - -import javafx.beans.property.ObjectProperty; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.settings.KeychainBackend; -import org.cryptomator.common.settings.Settings; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.EnumSet; -import java.util.Optional; - -/** - * A facade to LinuxSecretServiceKeychainAccessImpl and LinuxKDEWalletKeychainAccessImpl - * that depend on libraries that are unavailable on Mac and Windows. - */ -@Singleton -public class LinuxSystemKeychainAccess implements KeychainAccessStrategy { - - // the actual implementation is hidden in this delegate objects which are loaded via reflection, - // as it depends on libraries that aren't necessarily available: - private final Optional delegate; - private final Settings settings; - private static EnumSet availableKeychainBackends = EnumSet.noneOf(KeychainBackend.class); - private static KeychainBackend backendActivated = null; - private static boolean isGnomeKeyringAvailable; - private static boolean isKdeWalletAvailable; - - @Inject - public LinuxSystemKeychainAccess(Settings settings) { - this.settings = settings; - this.delegate = constructKeychainAccess(); - } - - private Optional constructKeychainAccess() { - try { // find out which backends are available - Class clazz = Class.forName("org.cryptomator.keychain.LinuxSecretServiceKeychainAccessImpl"); - KeychainAccessStrategy gnomeKeyring = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance(); - if (gnomeKeyring.isSupported()) { - LinuxSystemKeychainAccess.availableKeychainBackends.add(KeychainBackend.GNOME); - LinuxSystemKeychainAccess.isGnomeKeyringAvailable = true; - } - clazz = Class.forName("org.cryptomator.keychain.LinuxKDEWalletKeychainAccessImpl"); - KeychainAccessStrategy kdeWallet = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance(); - if (kdeWallet.isSupported()) { - LinuxSystemKeychainAccess.availableKeychainBackends.add(KeychainBackend.KDE); - LinuxSystemKeychainAccess.isKdeWalletAvailable = true; - } - - // load password backend setting as the preferred backend - ObjectProperty pwSetting = settings.keychainBackend(); - - // check for GNOME keyring first, as this gets precedence over - // KDE wallet as the former was implemented first - if (isGnomeKeyringAvailable && pwSetting.get().equals(KeychainBackend.GNOME)) { - pwSetting.setValue(KeychainBackend.GNOME); - LinuxSystemKeychainAccess.backendActivated = KeychainBackend.GNOME; - return Optional.of(gnomeKeyring); - } - - if (isKdeWalletAvailable && pwSetting.get().equals(KeychainBackend.KDE)) { - pwSetting.setValue(KeychainBackend.KDE); - LinuxSystemKeychainAccess.backendActivated = KeychainBackend.KDE; - return Optional.of(kdeWallet); - } - return Optional.empty(); - } catch (Exception e) { - return Optional.empty(); - } - } - - /* Getter/Setter */ - - public static EnumSet getAvailableKeychainBackends() { - return availableKeychainBackends; - } - - public static KeychainBackend getBackendActivated() { - return backendActivated; - } - - @Override - public boolean isSupported() { - return SystemUtils.IS_OS_LINUX && delegate.map(KeychainAccessStrategy::isSupported).orElse(false); - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - delegate.orElseThrow(IllegalStateException::new).storePassphrase(key, passphrase); - } - - @Override - public char[] loadPassphrase(String key) throws KeychainAccessException { - return delegate.orElseThrow(IllegalStateException::new).loadPassphrase(key); - } - - @Override - public void deletePassphrase(String key) throws KeychainAccessException { - delegate.orElseThrow(IllegalStateException::new).deletePassphrase(key); - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - delegate.orElseThrow(IllegalStateException::new).changePassphrase(key, passphrase); - } -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java deleted file mode 100644 index 39a6cd756..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.jni.MacFunctions; -import org.cryptomator.jni.MacKeychainAccess; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Optional; - -@Singleton -class MacSystemKeychainAccess implements KeychainAccessStrategy { - - private final Optional macFunctions; - - @Inject - public MacSystemKeychainAccess(Optional macFunctions) { - this.macFunctions = macFunctions; - } - - private MacKeychainAccess keychain() { - return macFunctions.orElseThrow(IllegalStateException::new).keychainAccess(); - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) { - keychain().storePassword(key, passphrase); - } - - @Override - public char[] loadPassphrase(String key) { - return keychain().loadPassword(key); - } - - @Override - public boolean isSupported() { - return SystemUtils.IS_OS_MAC_OSX && macFunctions.isPresent(); - } - - @Override - public void deletePassphrase(String key) { - keychain().deletePassword(key); - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) { - if (keychain().deletePassword(key)) { - keychain().storePassword(key, passphrase); - } - } - -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java deleted file mode 100644 index 6668104a4..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java +++ /dev/null @@ -1,209 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -import com.google.common.io.BaseEncoding; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.Environment; -import org.cryptomator.jni.WinDataProtection; -import org.cryptomator.jni.WinFunctions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; - -import static java.nio.charset.StandardCharsets.UTF_8; - -@Singleton -class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(WindowsProtectedKeychainAccess.class); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting() // - .registerTypeHierarchyAdapter(byte[].class, new ByteArrayJsonAdapter()) // - .disableHtmlEscaping().create(); - - private final Optional winFunctions; - private final List keychainPaths; - private Map keychainEntries; - - @Inject - public WindowsProtectedKeychainAccess(Optional winFunctions, Environment environment) { - this.winFunctions = winFunctions; - this.keychainPaths = environment.getKeychainPath().collect(Collectors.toList()); - } - - private WinDataProtection dataProtection() { - return winFunctions.orElseThrow(IllegalStateException::new).dataProtection(); - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) { - loadKeychainEntriesIfNeeded(); - ByteBuffer buf = UTF_8.encode(CharBuffer.wrap(passphrase)); - byte[] cleartext = new byte[buf.remaining()]; - buf.get(cleartext); - KeychainEntry entry = new KeychainEntry(); - entry.salt = generateSalt(); - entry.ciphertext = dataProtection().protect(cleartext, entry.salt); - Arrays.fill(buf.array(), (byte) 0x00); - Arrays.fill(cleartext, (byte) 0x00); - keychainEntries.put(key, entry); - saveKeychainEntries(); - } - - @Override - public char[] loadPassphrase(String key) { - loadKeychainEntriesIfNeeded(); - KeychainEntry entry = keychainEntries.get(key); - if (entry == null) { - return null; - } - byte[] cleartext = dataProtection().unprotect(entry.ciphertext, entry.salt); - if (cleartext == null) { - return null; - } - CharBuffer buf = UTF_8.decode(ByteBuffer.wrap(cleartext)); - char[] passphrase = new char[buf.remaining()]; - buf.get(passphrase); - Arrays.fill(cleartext, (byte) 0x00); - Arrays.fill(buf.array(), (char) 0x00); - return passphrase; - } - - @Override - public void deletePassphrase(String key) { - loadKeychainEntriesIfNeeded(); - keychainEntries.remove(key); - saveKeychainEntries(); - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) { - loadKeychainEntriesIfNeeded(); - if (keychainEntries.remove(key) != null) { - storePassphrase(key, passphrase); - } - } - - @Override - public boolean isSupported() { - return SystemUtils.IS_OS_WINDOWS && winFunctions.isPresent() && !keychainPaths.isEmpty(); - } - - private byte[] generateSalt() { - byte[] result = new byte[2 * Long.BYTES]; - UUID uuid = UUID.randomUUID(); - ByteBuffer buf = ByteBuffer.wrap(result); - buf.putLong(uuid.getMostSignificantBits()); - buf.putLong(uuid.getLeastSignificantBits()); - return result; - } - - private void loadKeychainEntriesIfNeeded() { - if (keychainEntries == null) { - for (Path keychainPath : keychainPaths) { - Optional> keychain = loadKeychainEntries(keychainPath); - if (keychain.isPresent()) { - keychainEntries = keychain.get(); - break; - } - } - } - if (keychainEntries == null) { - LOG.info("Unable to load existing keychain file, creating new keychain."); - keychainEntries = new HashMap<>(); - } - } - - private Optional> loadKeychainEntries(Path keychainPath) { - LOG.debug("Attempting to load keychain from {}", keychainPath); - Type type = new TypeToken>() { - }.getType(); - try (InputStream in = Files.newInputStream(keychainPath, StandardOpenOption.READ); // - Reader reader = new InputStreamReader(in, UTF_8)) { - return Optional.of(GSON.fromJson(reader, type)); - } catch (NoSuchFileException | JsonParseException e) { - return Optional.empty(); - } catch (IOException e) { - throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e); - } - } - - private void saveKeychainEntries() { - if (keychainPaths.isEmpty()) { - throw new IllegalStateException("Can't save keychain if no keychain path is specified."); - } - saveKeychainEntries(keychainPaths.get(0)); - } - - private void saveKeychainEntries(Path keychainPath) { - try (OutputStream out = Files.newOutputStream(keychainPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); // - Writer writer = new OutputStreamWriter(out, UTF_8)) { - GSON.toJson(keychainEntries, writer); - } catch (IOException e) { - throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e); - } - } - - private static class KeychainEntry { - - @SerializedName("ciphertext") - byte[] ciphertext; - @SerializedName("salt") - byte[] salt; - } - - private static class ByteArrayJsonAdapter implements JsonSerializer, JsonDeserializer { - - private static final BaseEncoding BASE64 = BaseEncoding.base64(); - - @Override - public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - return BASE64.decode(json.getAsString()); - } - - @Override - public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(BASE64.encode(src)); - } - - } - -} diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java b/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java deleted file mode 100644 index aa5c2b2f4..000000000 --- a/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -import org.cryptomator.common.Environment; -import org.cryptomator.jni.WinDataProtection; -import org.cryptomator.jni.WinFunctions; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.mockito.Mockito; -import org.mockito.stubbing.Answer; - -import java.nio.file.Path; -import java.util.Optional; -import java.util.stream.Stream; - -public class WindowsProtectedKeychainAccessTest { - - private WindowsProtectedKeychainAccess keychain; - - @BeforeEach - public void setup(@TempDir Path tempDir) { - Path keychainPath = tempDir.resolve("keychainfile.tmp"); - Environment env = Mockito.mock(Environment.class); - Mockito.when(env.getKeychainPath()).thenReturn(Stream.of(keychainPath)); - WinFunctions winFunctions = Mockito.mock(WinFunctions.class); - WinDataProtection winDataProtection = Mockito.mock(WinDataProtection.class); - Mockito.when(winFunctions.dataProtection()).thenReturn(winDataProtection); - Answer answerReturningFirstArg = invocation -> ((byte[]) invocation.getArgument(0)).clone(); - Mockito.when(winDataProtection.protect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg); - Mockito.when(winDataProtection.unprotect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg); - keychain = new WindowsProtectedKeychainAccess(Optional.of(winFunctions), env); - } - - @Test - public void testStoreAndLoad() { - String storedPw1 = "topSecret"; - String storedPw2 = "bottomSecret"; - keychain.storePassphrase("myPassword", storedPw1); - keychain.storePassphrase("myOtherPassword", storedPw2); - String loadedPw1 = new String(keychain.loadPassphrase("myPassword")); - String loadedPw2 = new String(keychain.loadPassphrase("myOtherPassword")); - Assertions.assertEquals(storedPw1, loadedPw1); - Assertions.assertEquals(storedPw2, loadedPw2); - keychain.deletePassphrase("myPassword"); - Assertions.assertNull(keychain.loadPassphrase("myPassword")); - Assertions.assertNotNull(keychain.loadPassphrase("myOtherPassword")); - Assertions.assertNull(keychain.loadPassphrase("nonExistingPassword")); - } - -} diff --git a/main/keychain/src/test/resources/log4j2.xml b/main/keychain/src/test/resources/log4j2.xml deleted file mode 100644 index 3e9a7bd67..000000000 --- a/main/keychain/src/test/resources/log4j2.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/main/pom.xml b/main/pom.xml index e30e843a3..838b871de 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -25,6 +25,10 @@ 1.9.12 + 0.1.4 + 0.1.0-beta1 + 0.1.0-beta1 + 0.1.0-beta1 2.2.3 1.2.5 1.1.15 @@ -64,11 +68,6 @@ commons ${project.version} - - org.cryptomator - keychain - ${project.version} - org.cryptomator ui @@ -107,6 +106,26 @@ ${cryptomator.webdav.version} + org.cryptomator + integrations-api + ${cryptomator.integrations.version} + + + org.cryptomator + integrations-win + ${cryptomator.integrations.win.version} + + + org.cryptomator + integrations-mac + ${cryptomator.integrations.mac.version} + + + org.cryptomator + integrations-linux + ${cryptomator.integrations.linux.version} + + org.cryptomator jni ${cryptomator.jni.version} @@ -163,18 +182,6 @@ ${commons-lang3.version} - - - de.swiesend - secret-service - ${secret-service.version} - - - org.purejava - kdewallet - ${kdewallet.version} - - com.auth0 @@ -256,7 +263,6 @@ commons - keychain ui launcher @@ -279,26 +285,87 @@ + + mac + + + mac + + + idea.version + + + + + org.cryptomator + integrations-mac + + + + + linux + + + unix + Linux + + + idea.version + + + + + org.cryptomator + integrations-linux + + + + + windows + + + windows + + + idea.version + + + + + org.cryptomator + integrations-win + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + + org.apache.maven.plugins maven-dependency-plugin 3.1.2 - - - copy-libs - - copy-dependencies - - - ${project.build.directory}/libs - runtime - - - + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 org.codehaus.mojo @@ -348,7 +415,6 @@ maven-compiler-plugin - 3.8.1 14 @@ -363,7 +429,6 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 diff --git a/main/ui/pom.xml b/main/ui/pom.xml index 79b59eeec..7784ee802 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -10,10 +10,6 @@ Cryptomator GUI - - org.cryptomator - keychain - org.cryptomator commons diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java index e1fc9c093..52cfe2b81 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java @@ -1,10 +1,10 @@ package org.cryptomator.ui.changepassword; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxController; @@ -36,14 +36,14 @@ public class ChangePasswordController implements FxController { private final Vault vault; private final ObjectProperty newPassword; private final ErrorComponent.Builder errorComponent; - private final Optional keychain; + private final KeychainManager keychain; public NiceSecurePasswordField oldPasswordField; public CheckBox finalConfirmationCheckbox; public Button finishButton; @Inject - public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty newPassword, ErrorComponent.Builder errorComponent, Optional keychain) { + public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty newPassword, ErrorComponent.Builder errorComponent, KeychainManager keychain) { this.window = window; this.vault = vault; this.newPassword = newPassword; @@ -82,9 +82,9 @@ public class ChangePasswordController implements FxController { } private void updatePasswordInSystemkeychain() { - if (keychain.isPresent()) { + if (keychain.isSupported()) { try { - keychain.get().changePassphrase(vault.getId(), CharBuffer.wrap(newPassword.get())); + keychain.changePassphrase(vault.getId(), CharBuffer.wrap(newPassword.get())); LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayName()); } catch (KeychainAccessException e) { LOG.error("Failed to update password in system keychain.", e); diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java index 2631c0f6a..35ed49870 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java @@ -3,7 +3,6 @@ package org.cryptomator.ui.common; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.Volume; -import org.cryptomator.keychain.KeychainManager; import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,7 +13,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; @@ -25,12 +23,10 @@ public class VaultService { private static final Logger LOG = LoggerFactory.getLogger(VaultService.class); private final ExecutorService executorService; - private final Optional keychain; @Inject - public VaultService(ExecutorService executorService, Optional keychain) { + public VaultService(ExecutorService executorService) { this.executorService = executorService; - this.keychain = keychain; } public void reveal(Vault vault) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java index 99f8cbc41..81d28f682 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java @@ -1,8 +1,8 @@ package org.cryptomator.ui.forgetPassword; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.FxController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,7 +11,6 @@ import javax.inject.Inject; import javafx.beans.property.BooleanProperty; import javafx.fxml.FXML; import javafx.stage.Stage; -import java.util.Optional; @ForgetPasswordScoped public class ForgetPasswordController implements FxController { @@ -20,11 +19,11 @@ public class ForgetPasswordController implements FxController { private final Stage window; private final Vault vault; - private final Optional keychain; + private final KeychainManager keychain; private final BooleanProperty confirmedResult; @Inject - public ForgetPasswordController(@ForgetPasswordWindow Stage window, @ForgetPasswordWindow Vault vault, Optional keychain, @ForgetPasswordWindow BooleanProperty confirmedResult) { + public ForgetPasswordController(@ForgetPasswordWindow Stage window, @ForgetPasswordWindow Vault vault, KeychainManager keychain, @ForgetPasswordWindow BooleanProperty confirmedResult) { this.window = window; this.vault = vault; this.keychain = keychain; @@ -38,9 +37,9 @@ public class ForgetPasswordController implements FxController { @FXML public void finish() { - if (keychain.isPresent()) { + if (keychain.isSupported()) { try { - keychain.get().deletePassphrase(vault.getId()); + keychain.deletePassphrase(vault.getId()); LOG.debug("Forgot password for vault {}.", vault.getDisplayName()); confirmedResult.setValue(true); } catch (KeychainAccessException e) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java b/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java index 9b8d6e776..2ac585763 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java @@ -3,7 +3,6 @@ package org.cryptomator.ui.launcher; import dagger.Module; import dagger.Provides; import org.cryptomator.common.JniModule; -import org.cryptomator.keychain.KeychainModule; import org.cryptomator.ui.fxapp.FxApplicationComponent; import org.cryptomator.ui.traymenu.TrayMenuComponent; @@ -13,7 +12,7 @@ import java.util.ResourceBundle; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -@Module(includes = {JniModule.class, KeychainModule.class}, subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class}) +@Module(includes = {JniModule.class}, subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class}) public abstract class UiLauncherModule { @Provides diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java index 367aacc3a..e7da43938 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java @@ -1,8 +1,8 @@ package org.cryptomator.ui.mainwindow; import com.tobiasdiez.easybind.EasyBind; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainManager; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; @@ -22,19 +22,19 @@ public class VaultDetailLockedController implements FxController { private final ReadOnlyObjectProperty vault; private final FxApplication application; private final VaultOptionsComponent.Builder vaultOptionsWindow; - private final Optional keychainManagerOptional; + private final KeychainManager keychain; private final Stage mainWindow; private final BooleanExpression passwordSaved; @Inject - VaultDetailLockedController(ObjectProperty vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, Optional keychainManagerOptional, @MainWindow Stage mainWindow) { + VaultDetailLockedController(ObjectProperty vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) { this.vault = vault; this.application = application; this.vaultOptionsWindow = vaultOptionsWindow; - this.keychainManagerOptional = keychainManagerOptional; + this.keychain = keychain; this.mainWindow = mainWindow; - if (keychainManagerOptional.isPresent()) { - this.passwordSaved = BooleanExpression.booleanExpression(EasyBind.select(vault).selectObject(v -> keychainManagerOptional.get().getPassphraseStoredProperty(v.getId()))); + if (keychain.isSupported()) { + this.passwordSaved = BooleanExpression.booleanExpression(EasyBind.select(vault).selectObject(v -> keychain.getPassphraseStoredProperty(v.getId()))); } else { this.passwordSaved = new SimpleBooleanProperty(false); } @@ -65,8 +65,8 @@ public class VaultDetailLockedController implements FxController { } public boolean isPasswordSaved() { - if (keychainManagerOptional.isPresent() && vault.get() != null) { - return keychainManagerOptional.get().getPassphraseStoredProperty(vault.get().getId()).get(); + if (keychain.isSupported() && vault.get() != null) { + return keychain.getPassphraseStoredProperty(vault.get().getId()).get(); } else return false; } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java index 2bf4f49a1..830e15819 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java @@ -1,6 +1,7 @@ package org.cryptomator.ui.migration; import dagger.Lazy; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.cryptofs.FileNameTooLongException; @@ -9,8 +10,7 @@ import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.cryptofs.migration.api.MigrationContinuationListener; import org.cryptomator.cryptofs.migration.api.MigrationProgressListener; import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxController; @@ -37,7 +37,6 @@ import javafx.scene.Scene; import javafx.scene.control.ContentDisplay; import javafx.stage.Stage; import java.util.Arrays; -import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -55,7 +54,7 @@ public class MigrationRunController implements FxController { private final Vault vault; private final ExecutorService executor; private final ScheduledExecutorService scheduler; - private final Optional keychain; + private final KeychainManager keychain; private final ObjectProperty missingCapability; private final ErrorComponent.Builder errorComponent; private final Lazy startScene; @@ -69,8 +68,7 @@ public class MigrationRunController implements FxController { public NiceSecurePasswordField passwordField; @Inject - public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, Optional keychain, @Named("capabilityErrorCause") ObjectProperty missingCapability, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_IMPOSSIBLE) Lazy impossibleScene, ErrorComponent.Builder errorComponent) { - + public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, KeychainManager keychain, @Named("capabilityErrorCause") ObjectProperty missingCapability, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_IMPOSSIBLE) Lazy impossibleScene, ErrorComponent.Builder errorComponent) { this.window = window; this.vault = vault; this.executor = executor; @@ -88,7 +86,7 @@ public class MigrationRunController implements FxController { } public void initialize() { - if (keychain.isPresent()) { + if (keychain.isSupported()) { loadStoredPassword(); } migrationButtonDisabled.bind(vault.stateProperty().isNotEqualTo(VaultState.NEEDS_MIGRATION).or(passwordField.textProperty().isEmpty())); @@ -167,10 +165,10 @@ public class MigrationRunController implements FxController { } private void loadStoredPassword() { - assert keychain.isPresent(); + assert keychain.isSupported(); char[] storedPw = null; try { - storedPw = keychain.get().loadPassphrase(vault.getId()); + storedPw = keychain.loadPassphrase(vault.getId()); if (storedPw != null) { passwordField.setPassword(storedPw); passwordField.selectRange(storedPw.length, storedPw.length); diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java index 4e4abfbc9..208605f51 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java @@ -1,13 +1,11 @@ package org.cryptomator.ui.preferences; -import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Environment; import org.cryptomator.common.LicenseHolder; import org.cryptomator.common.settings.KeychainBackend; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.UiTheme; -import org.cryptomator.keychain.KeychainAccessStrategy; -import org.cryptomator.keychain.LinuxSystemKeychainAccess; +import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.ui.common.FxController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,12 +25,12 @@ import javafx.scene.control.RadioButton; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.util.StringConverter; - import java.util.Arrays; -import java.util.EnumSet; import java.util.Optional; import java.util.ResourceBundle; +import java.util.Set; import java.util.concurrent.ExecutorService; +import java.util.stream.Collectors; @PreferencesScoped public class GeneralPreferencesController implements FxController { @@ -48,7 +46,7 @@ public class GeneralPreferencesController implements FxController { private final ResourceBundle resourceBundle; private final Application application; private final Environment environment; - private Optional keychain; + private final Set keychainAccessProviders; public ChoiceBox themeChoiceBox; public ChoiceBox keychainBackendChoiceBox; public CheckBox startHiddenCheckbox; @@ -59,11 +57,11 @@ public class GeneralPreferencesController implements FxController { public RadioButton nodeOrientationRtl; @Inject - GeneralPreferencesController(Settings settings, @Named("trayMenuSupported") boolean trayMenuSupported, Optional autoStartStrategy, Optional keychain, ObjectProperty selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment) { + GeneralPreferencesController(Settings settings, @Named("trayMenuSupported") boolean trayMenuSupported, Optional autoStartStrategy, Set keychainAccessProviders, ObjectProperty selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment) { this.settings = settings; this.trayMenuSupported = trayMenuSupported; this.autoStartStrategy = autoStartStrategy; - this.keychain = keychain; + this.keychainAccessProviders = keychainAccessProviders; this.selectedTabProperty = selectedTabProperty; this.licenseHolder = licenseHolder; this.executor = executor; @@ -96,16 +94,15 @@ public class GeneralPreferencesController implements FxController { nodeOrientation.selectedToggleProperty().addListener(this::toggleNodeOrientation); keychainBackendChoiceBox.getItems().addAll(getAvailableBackends()); - if (keychain.isPresent() && SystemUtils.IS_OS_LINUX) { - keychainBackendChoiceBox.setValue(LinuxSystemKeychainAccess.getBackendActivated()); - } - if (keychain.isPresent() && (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS)) { - keychainBackendChoiceBox.setValue(Arrays.stream(KeychainBackend.supportedBackends()).findFirst().orElseThrow(IllegalStateException::new)); - } keychainBackendChoiceBox.setConverter(new KeychainBackendConverter(resourceBundle)); keychainBackendChoiceBox.valueProperty().bindBidirectional(settings.keychainBackend()); } + private KeychainBackend[] getAvailableBackends() { + var namesOfAvailableProviders = keychainAccessProviders.stream().map(KeychainAccessProvider::getClass).map(Class::getName).collect(Collectors.toUnmodifiableSet()); + return Arrays.stream(KeychainBackend.values()).filter(value -> namesOfAvailableProviders.contains(value.getProviderClass())).toArray(KeychainBackend[]::new); + } + public boolean isTrayMenuSupported() { return this.trayMenuSupported; } @@ -183,7 +180,7 @@ public class GeneralPreferencesController implements FxController { @Override public String toString(KeychainBackend impl) { - return resourceBundle.getString(impl.getDisplayName()); + return resourceBundle.getString("preferences.general.keychainBackend." + impl.getProviderClass()); } @Override @@ -215,17 +212,4 @@ public class GeneralPreferencesController implements FxController { } } - private KeychainBackend[] getAvailableBackends() { - if (!keychain.isPresent()) { - return new KeychainBackend[]{}; - } - if (SystemUtils.IS_OS_LINUX) { - EnumSet backends = LinuxSystemKeychainAccess.getAvailableKeychainBackends(); - return backends.toArray(KeychainBackend[]::new); - } - if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) { - return KeychainBackend.supportedBackends(); - } - return new KeychainBackend[]{}; - } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java index b64195d9a..5460cc149 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -1,7 +1,7 @@ package org.cryptomator.ui.unlock; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainManager; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.common.WeakBindings; @@ -49,7 +49,7 @@ public class UnlockController implements FxController { private final Optional savedPassword; private final UserInteractionLock passwordEntryLock; private final ForgetPasswordComponent.Builder forgetPassword; - private final Optional keychain; + private final KeychainManager keychain; private final ObjectBinding unlockButtonContentDisplay; private final BooleanBinding userInteractionDisabled; private final BooleanProperty unlockButtonDisabled; @@ -65,7 +65,7 @@ public class UnlockController implements FxController { public Animation unlockAnimation; @Inject - public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, ForgetPasswordComponent.Builder forgetPassword, Optional keychain) { + public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, ForgetPasswordComponent.Builder forgetPassword, KeychainManager keychain) { this.window = window; this.vault = vault; this.password = password; @@ -214,6 +214,6 @@ public class UnlockController implements FxController { } public boolean isKeychainAccessAvailable() { - return keychain.isPresent(); + return keychain.isSupported(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java index 102743e7f..da5e2884d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java @@ -4,9 +4,9 @@ import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.FXMLLoaderFactory; import org.cryptomator.ui.common.FxController; @@ -49,15 +49,17 @@ abstract class UnlockModule { @Provides @Named("savedPassword") @UnlockScoped - static Optional provideStoredPassword(Optional keychain, @UnlockWindow Vault vault) { - return keychain.map(k -> { + static Optional provideStoredPassword(KeychainManager keychain, @UnlockWindow Vault vault) { + if (!keychain.isSupported()) { + return Optional.empty(); + } else { try { - return k.loadPassphrase(vault.getId()); + return Optional.ofNullable(keychain.loadPassphrase(vault.getId())); } catch (KeychainAccessException e) { LOG.error("Failed to load entry from system keychain.", e); - return null; + return Optional.empty(); } - }); + } } @Provides diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java index bdefb4ad3..9b95103ad 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java @@ -1,14 +1,14 @@ package org.cryptomator.ui.unlock; import dagger.Lazy; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.mountpoint.InvalidMountPointException; import org.cryptomator.common.vaults.MountPointRequirement; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.Volume.VolumeException; import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxmlFile; @@ -53,14 +53,14 @@ public class UnlockWorkflow extends Task { private final AtomicBoolean savePassword; private final Optional savedPassword; private final UserInteractionLock passwordEntryLock; - private final Optional keychain; + private final KeychainManager keychain; private final Lazy unlockScene; private final Lazy successScene; private final Lazy invalidMountPointScene; private final ErrorComponent.Builder errorComponent; @Inject - UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, Optional keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, ErrorComponent.Builder errorComponent) { + UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, KeychainManager keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, ErrorComponent.Builder errorComponent) { this.window = window; this.vault = vault; this.vaultService = vaultService; @@ -150,9 +150,9 @@ public class UnlockWorkflow extends Task { } private void savePasswordToSystemkeychain() { - if (keychain.isPresent()) { + if (keychain.isSupported()) { try { - keychain.get().storePassphrase(vault.getId(), CharBuffer.wrap(password.get())); + keychain.storePassphrase(vault.getId(), CharBuffer.wrap(password.get())); } catch (KeychainAccessException e) { LOG.error("Failed to store passphrase in system keychain.", e); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MasterkeyOptionsController.java b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MasterkeyOptionsController.java index 4c08ad75a..ee38e1f18 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MasterkeyOptionsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MasterkeyOptionsController.java @@ -1,11 +1,13 @@ package org.cryptomator.ui.vaultoptions; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.changepassword.ChangePasswordComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.recoverykey.RecoveryKeyComponent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.beans.binding.Bindings; @@ -13,29 +15,32 @@ import javafx.beans.binding.BooleanExpression; import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; import javafx.stage.Stage; -import java.util.Optional; @VaultOptionsScoped public class MasterkeyOptionsController implements FxController { + private static final Logger LOG = LoggerFactory.getLogger(MasterkeyOptionsController.class); + private final Vault vault; private final Stage window; private final ChangePasswordComponent.Builder changePasswordWindow; private final RecoveryKeyComponent.Builder recoveryKeyWindow; - private final Optional keychainManagerOptional; + private final KeychainManager keychain; private final BooleanExpression passwordSaved; @Inject - MasterkeyOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Builder recoveryKeyWindow, Optional keychainManagerOptional) { + MasterkeyOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Builder recoveryKeyWindow, KeychainManager keychain) { this.vault = vault; this.window = window; this.changePasswordWindow = changePasswordWindow; this.recoveryKeyWindow = recoveryKeyWindow; - this.keychainManagerOptional = keychainManagerOptional; - if (keychainManagerOptional.isPresent()) { - this.passwordSaved = Bindings.createBooleanBinding(this::isPasswordSaved, keychainManagerOptional.get().getPassphraseStoredProperty(vault.getId())); - } else this.passwordSaved = new SimpleBooleanProperty(false); + this.keychain = keychain; + if (keychain.isSupported()) { + this.passwordSaved = Bindings.createBooleanBinding(this::isPasswordSaved, keychain.getPassphraseStoredProperty(vault.getId())); + } else { + this.passwordSaved = new SimpleBooleanProperty(false); + } } @FXML @@ -54,8 +59,13 @@ public class MasterkeyOptionsController implements FxController { } @FXML - public void removePasswordFromKeychain() throws KeychainAccessException { - keychainManagerOptional.get().deletePassphrase(vault.getId()); + public void removePasswordFromKeychain() { + assert keychain.isSupported(); + try { + keychain.deletePassphrase(vault.getId()); + } catch (KeychainAccessException e) { + LOG.error("Failed to delete passphrase from system keychain.", e); + } window.close(); } @@ -64,8 +74,8 @@ public class MasterkeyOptionsController implements FxController { } public boolean isPasswordSaved() { - if (keychainManagerOptional.isPresent() && vault != null) { - return keychainManagerOptional.get().getPassphraseStoredProperty(vault.getId()).get(); + if (keychain.isSupported() && vault != null) { + return keychain.getPassphraseStoredProperty(vault.getId()).get(); } else return false; } } diff --git a/main/ui/src/main/resources/i18n/strings.properties b/main/ui/src/main/resources/i18n/strings.properties index 3805399aa..e391d263a 100644 --- a/main/ui/src/main/resources/i18n/strings.properties +++ b/main/ui/src/main/resources/i18n/strings.properties @@ -144,10 +144,10 @@ preferences.general.debugLogging=Enable debug logging preferences.general.debugDirectory=Reveal log files preferences.general.autoStart=Launch Cryptomator on system start preferences.general.keychainBackend=Store passwords with -preferences.general.keychainBackend.gnome=Gnome Keyring -preferences.general.keychainBackend.kde=KDE KWallet -preferences.general.keychainBackend.macSystemKeychain=macOS Keychain Access -preferences.general.keychainBackend.winSystemKeychain=Windows Data Protection Keychain +preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Gnome Keyring +preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=KDE KWallet +preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=macOS Keychain Access +preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Windows Data Protection Keychain preferences.general.interfaceOrientation=Interface Orientation preferences.general.interfaceOrientation.ltr=Left to Right preferences.general.interfaceOrientation.rtl=Right to Left diff --git a/main/ui/src/main/resources/license/THIRD-PARTY.txt b/main/ui/src/main/resources/license/THIRD-PARTY.txt index 3674e5e05..1389d25a1 100644 --- a/main/ui/src/main/resources/license/THIRD-PARTY.txt +++ b/main/ui/src/main/resources/license/THIRD-PARTY.txt @@ -11,82 +11,75 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. -Cryptomator uses 53 third-party dependencies under the following licenses: - Apache License v2.0: - - HKDF-RFC5869 (at.favre.lib:hkdf:1.1.0 - https://github.com/patrickfav/hkdf) - - jffi (com.github.jnr:jffi:1.2.23 - http://github.com/jnr/jffi) - - jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm) - - jnr-constants (com.github.jnr:jnr-constants:0.9.15 - http://github.com/jnr/jnr-constants) - - jnr-enxio (com.github.jnr:jnr-enxio:0.28 - http://github.com/jnr/jnr-enxio) - - jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - http://github.com/jnr/jnr-ffi) - - jnr-unixsocket (com.github.jnr:jnr-unixsocket:0.33 - http://github.com/jnr/jnr-unixsocket) - - FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/) - - Gson (com.google.code.gson:gson:2.8.6 - https://github.com/google/gson/gson) - - Dagger (com.google.dagger:dagger:2.22 - https://github.com/google/dagger) - - error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations) - - Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - https://github.com/google/guava/failureaccess) - - Guava: Google Core Libraries for Java (com.google.guava:guava:29.0-jre - https://github.com/google/guava/guava) - - Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture) - - J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - https://github.com/google/j2objc/) - - Apache Commons CLI (commons-cli:commons-cli:1.4 - http://commons.apache.org/proper/commons-cli/) - - Apache Commons IO (commons-io:commons-io:2.6 - http://commons.apache.org/proper/commons-io/) - - javax.inject (javax.inject:javax.inject:1 - http://code.google.com/p/atinject/) - - Java Native Access (net.java.dev.jna:jna:5.1.0 - https://github.com/java-native-access/jna) - - Java Native Access Platform (net.java.dev.jna:jna-platform:5.1.0 - https://github.com/java-native-access/jna) - - Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - https://commons.apache.org/proper/commons-lang/) - - Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.13 - http://hc.apache.org/httpcomponents-core-ga) - - Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.3 - http://jackrabbit.apache.org/jackrabbit-webdav/) - - Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.31.v20200723 - http://www.eclipse.org/jetty) - BSD: - - asm (org.ow2.asm:asm:7.1 - http://asm.ow2.org/) - - asm-analysis (org.ow2.asm:asm-analysis:7.1 - http://asm.ow2.org/) - - asm-commons (org.ow2.asm:asm-commons:7.1 - http://asm.ow2.org/) - - asm-tree (org.ow2.asm:asm-tree:7.1 - http://asm.ow2.org/) - - asm-util (org.ow2.asm:asm-util:7.1 - http://asm.ow2.org/) - Eclipse Public License - Version 1.0: - - Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.31.v20200723 - http://www.eclipse.org/jetty) - - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.31.v20200723 - http://www.eclipse.org/jetty) - Eclipse Public License - v 2.0: - - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix) - GPLv2: - - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix) - GPLv2+CE: - - Java Servlet API (javax.servlet:javax.servlet-api:3.1.0 - http://servlet-spec.java.net) - - javafx-base (org.openjfx:javafx-base:14 - https://openjdk.java.net/projects/openjfx/javafx-base/) - - javafx-controls (org.openjfx:javafx-controls:14 - https://openjdk.java.net/projects/openjfx/javafx-controls/) - - javafx-fxml (org.openjfx:javafx-fxml:14 - https://openjdk.java.net/projects/openjfx/javafx-fxml/) - - javafx-graphics (org.openjfx:javafx-graphics:14 - https://openjdk.java.net/projects/openjfx/javafx-graphics/) - LGPL 2.1: - - dbus-java (com.github.hypfvieh:dbus-java:3.2.3 - https://github.com/hypfvieh/dbus-java/dbus-java) - - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix) - - Java Native Access (net.java.dev.jna:jna:5.1.0 - https://github.com/java-native-access/jna) - - Java Native Access Platform (net.java.dev.jna:jna-platform:5.1.0 - https://github.com/java-native-access/jna) - MIT License: - - java jwt (com.auth0:java-jwt:3.10.3 - https://github.com/auth0/java-jwt) - - java-utils (com.github.hypfvieh:java-utils:1.0.6 - https://github.com/hypfvieh/java-utils) - - jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - http://github.com/jnr/jnr-x86asm) - - jnr-fuse (com.github.serceman:jnr-fuse:0.5.4 - no url defined) - - zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - https://github.com/nulab/zxcvbn4j) - - secret-service (de.swiesend:secret-service:1.1.0 - https://github.com/swiesend/secret-service) - - Checker Qual (org.checkerframework:checker-qual:2.11.1 - https://checkerframework.org) - - kdewallet (org.purejava:kdewallet:1.1.1 - https://github.com/purejava/kdewallet) - - SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - http://www.slf4j.org) - The BSD 2-Clause License: - - EasyBind (com.tobiasdiez:easybind:2.1.0 - https://github.com/tobiasdiez/EasyBind) +Cryptomator uses 46 third-party dependencies under the following licenses: + Apache License v2.0: + - jffi (com.github.jnr:jffi:1.2.23 - http://github.com/jnr/jffi) + - jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm) + - jnr-constants (com.github.jnr:jnr-constants:0.9.15 - http://github.com/jnr/jnr-constants) + - jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - http://github.com/jnr/jnr-ffi) + - FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/) + - Gson (com.google.code.gson:gson:2.8.6 - https://github.com/google/gson/gson) + - Dagger (com.google.dagger:dagger:2.22 - https://github.com/google/dagger) + - error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations) + - Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - https://github.com/google/guava/failureaccess) + - Guava: Google Core Libraries for Java (com.google.guava:guava:30.0-jre - https://github.com/google/guava/guava) + - Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture) + - J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - https://github.com/google/j2objc/) + - Apache Commons CLI (commons-cli:commons-cli:1.4 - http://commons.apache.org/proper/commons-cli/) + - Apache Commons IO (commons-io:commons-io:2.6 - http://commons.apache.org/proper/commons-io/) + - javax.inject (javax.inject:javax.inject:1 - http://code.google.com/p/atinject/) + - Java Native Access (net.java.dev.jna:jna:5.1.0 - https://github.com/java-native-access/jna) + - Java Native Access Platform (net.java.dev.jna:jna-platform:5.1.0 - https://github.com/java-native-access/jna) + - Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - https://commons.apache.org/proper/commons-lang/) + - Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.13 - http://hc.apache.org/httpcomponents-core-ga) + - Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.3 - http://jackrabbit.apache.org/jackrabbit-webdav/) + - Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.31.v20200723 - http://www.eclipse.org/jetty) + BSD: + - asm (org.ow2.asm:asm:7.1 - http://asm.ow2.org/) + - asm-analysis (org.ow2.asm:asm-analysis:7.1 - http://asm.ow2.org/) + - asm-commons (org.ow2.asm:asm-commons:7.1 - http://asm.ow2.org/) + - asm-tree (org.ow2.asm:asm-tree:7.1 - http://asm.ow2.org/) + - asm-util (org.ow2.asm:asm-util:7.1 - http://asm.ow2.org/) + Eclipse Public License - Version 1.0: + - Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.31.v20200723 - http://www.eclipse.org/jetty) + - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.31.v20200723 - http://www.eclipse.org/jetty) + Eclipse Public License - v 2.0: + - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix) + GPLv2: + - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix) + GPLv2+CE: + - Java Servlet API (javax.servlet:javax.servlet-api:3.1.0 - http://servlet-spec.java.net) + - javafx-base (org.openjfx:javafx-base:14 - https://openjdk.java.net/projects/openjfx/javafx-base/) + - javafx-controls (org.openjfx:javafx-controls:14 - https://openjdk.java.net/projects/openjfx/javafx-controls/) + - javafx-fxml (org.openjfx:javafx-fxml:14 - https://openjdk.java.net/projects/openjfx/javafx-fxml/) + - javafx-graphics (org.openjfx:javafx-graphics:14 - https://openjdk.java.net/projects/openjfx/javafx-graphics/) + LGPL 2.1: + - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix) + - Java Native Access (net.java.dev.jna:jna:5.1.0 - https://github.com/java-native-access/jna) + - Java Native Access Platform (net.java.dev.jna:jna-platform:5.1.0 - https://github.com/java-native-access/jna) + MIT License: + - java jwt (com.auth0:java-jwt:3.10.3 - https://github.com/auth0/java-jwt) + - jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - http://github.com/jnr/jnr-x86asm) + - jnr-fuse (com.github.serceman:jnr-fuse:0.5.4 - no url defined) + - zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - https://github.com/nulab/zxcvbn4j) + - Checker Qual (org.checkerframework:checker-qual:3.5.0 - https://checkerframework.org) + - SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - http://www.slf4j.org) + The BSD 2-Clause License: + - EasyBind (com.tobiasdiez:easybind:2.1.0 - https://github.com/tobiasdiez/EasyBind) Cryptomator uses other third-party assets under the following licenses: - SIL OFL 1.1 License: - - Font Awesome 5.12.0 (https://fontawesome.com/) +SIL OFL 1.1 License: +- Font Awesome 5.12.0 (https://fontawesome.com/)