Browse Source

Merge branch 'release/1.4.14'

Sebastian Stenzel 5 years ago
parent
commit
7fee6ad52f

+ 17 - 2
README.md

@@ -1,4 +1,4 @@
-![cryptomator](cryptomator.png)
+[![cryptomator](cryptomator.png)](https://cryptomator.org/)
 
 [![Build Status](https://travis-ci.org/cryptomator/cryptomator.svg?branch=master)](https://travis-ci.org/cryptomator/cryptomator)
 [![Known Vulnerabilities](https://snyk.io/test/github/cryptomator/cryptomator/badge.svg?targetFile=main%2Fpom.xml)](https://snyk.io/test/github/cryptomator/cryptomator?targetFile=main%2Fpom.xml)
@@ -8,7 +8,22 @@
 [![Latest Release](https://img.shields.io/github/release/cryptomator/cryptomator.svg)](https://github.com/cryptomator/cryptomator/releases/latest)
 [![Community](https://img.shields.io/badge/help-Community-orange.svg)](https://community.cryptomator.org)
 
-Multi-platform transparent client-side encryption of your files in the cloud.
+## Supporting Cryptomator
+
+Cryptomator is provided free of charge as an open-source project despite the high development effort and is therefore dependent on donations. If you are also interested in further development, we offer you the opportunity to support us:
+
+- [One-time or recurring donation via Cryptomator's website.](https://cryptomator.org/#donate)
+- [Become a sponsor via Cryptomator's sponsors website.](https://cryptomator.org/sponsors/)
+
+### Silver Sponsors
+
+[![TheBestVPN](https://cryptomator.org/img/sponsors/thebestvpn.png)](https://thebestvpn.com/)
+
+---
+
+## Introduction
+
+Cryptomator offers multi-platform transparent client-side encryption of your files in the cloud.
 
 Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator.org/) or clone and build Cryptomator using Maven (instructions below).
 

+ 1 - 1
main/buildkit/pom.xml

@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>org.cryptomator</groupId>
 		<artifactId>main</artifactId>
-		<version>1.4.13</version>
+		<version>1.4.14</version>
 	</parent>
 	<artifactId>buildkit</artifactId>
 	<packaging>pom</packaging>

+ 1 - 1
main/commons/pom.xml

@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>org.cryptomator</groupId>
 		<artifactId>main</artifactId>
-		<version>1.4.13</version>
+		<version>1.4.14</version>
 	</parent>
 	<artifactId>commons</artifactId>
 	<name>Cryptomator Commons</name>

+ 1 - 1
main/keychain/pom.xml

@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>org.cryptomator</groupId>
 		<artifactId>main</artifactId>
-		<version>1.4.13</version>
+		<version>1.4.14</version>
 	</parent>
 	<artifactId>keychain</artifactId>
 	<name>System Keychain Access</name>

+ 0 - 9
main/keychain/src/main/java/org/cryptomator/keychain/GnomeKeyringAccess.java

@@ -1,9 +0,0 @@
-package org.cryptomator.keychain;
-
-public interface GnomeKeyringAccess {
-	public void storePassword(String key, CharSequence passphrase);
-
-	public char[] loadPassword(String key);
-
-	public void deletePassword(String key);
-}

+ 0 - 52
main/keychain/src/main/java/org/cryptomator/keychain/GnomeKeyringAccessImpl.java

@@ -1,52 +0,0 @@
-package org.cryptomator.keychain;
-
-import org.freedesktop.secret.simple.SimpleCollection;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class GnomeKeyringAccessImpl implements GnomeKeyringAccess {
-	private static final Logger LOG = LoggerFactory.getLogger(GnomeKeyringAccessImpl.class);
-	private SimpleCollection keyring;
-
-	public GnomeKeyringAccessImpl() {
-		try {
-			keyring = new SimpleCollection();
-		} catch (IOException e) {
-			LOG.error("D-Bus reports a problem.", e);
-		}
-	}
-
-	public void storePassword(String key, CharSequence passphrase) {
-		List<String> list = keyring.getItems(createAttributes(key));
-		if (list == null) {
-			keyring.createItem("Cryptomator", passphrase, createAttributes(key));
-		}
-	}
-
-	public char[] loadPassword(String key) {
-		List<String> list = keyring.getItems(createAttributes(key));
-		if (list != null) {
-			return keyring.getSecret(list.get(0));
-		} else {
-			return null;
-		}
-	}
-
-	public void deletePassword(String key) {
-		List<String> list = keyring.getItems(createAttributes(key));
-		if (list != null) {
-			keyring.deleteItem(list.get(0));
-		}
-	}
-
-	private Map<String, String> createAttributes(String key) {
-		Map<String, String> attributes = new HashMap();
-		attributes.put("Vault", key);
-		return attributes;
-	}
-}

+ 3 - 3
main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccess.java

@@ -13,19 +13,19 @@ public interface KeychainAccess {
 	 * @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);
+	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 <code>null</code> if no value for the given key could be found.
 	 */
-	char[] loadPassphrase(String key);
+	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);
+	void deletePassphrase(String key) throws KeychainAccessException;
 
 }

+ 12 - 0
main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessException.java

@@ -0,0 +1,12 @@
+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);
+	}
+
+}

+ 1 - 1
main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java

@@ -31,7 +31,7 @@ public class KeychainModule {
 
 	@Provides
 	@ElementsIntoSet
-	Set<KeychainAccessStrategy> provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsProtectedKeychainAccess winKeychain, LinuxSecretServiceAccess linKeychain) {
+	Set<KeychainAccessStrategy> provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsProtectedKeychainAccess winKeychain, LinuxSecretServiceKeychainAccess linKeychain) {
 		return Sets.newHashSet(macKeychain, winKeychain, linKeychain);
 	}
 

+ 0 - 28
main/keychain/src/main/java/org/cryptomator/keychain/LinuxKeychainTester.java

@@ -1,28 +0,0 @@
-package org.cryptomator.keychain;
-
-import org.apache.commons.lang3.SystemUtils;
-
-import java.util.Optional;
-
-public class LinuxKeychainTester {
-
-	public static boolean secretServiceIsAvailable() {
-		try {
-			Class.forName("org.freedesktop.secret.simple.SimpleCollection");
-			return SystemUtils.IS_OS_LINUX; // even if the classes could be loaded, secretService is only available on linux
-		} catch (ClassNotFoundException e) {
-			return false;
-		}
-	}
-
-	public static Optional<GnomeKeyringAccess> getSecretService() {
-		if (!secretServiceIsAvailable()) return Optional.empty();
-		try {
-			Class clazz = Class.forName("org.cryptomator.keychain.GnomeKeyringAccessImpl");
-			GnomeKeyringAccess keyring = (GnomeKeyringAccess) clazz.getDeclaredConstructor().newInstance();
-			return Optional.of(keyring);
-		} catch (Exception e) {
-			return Optional.empty();
-		}
-	}
-}

+ 0 - 39
main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceAccess.java

@@ -1,39 +0,0 @@
-package org.cryptomator.keychain;
-
-import com.google.common.base.Preconditions;
-import org.apache.commons.lang3.SystemUtils;
-
-import javax.inject.Inject;
-import java.util.Optional;
-
-public class LinuxSecretServiceAccess implements KeychainAccessStrategy {
-	private final Optional<GnomeKeyringAccess> gnomeLoginKeyring;
-
-	@Inject
-	public LinuxSecretServiceAccess() {
-		gnomeLoginKeyring = LinuxKeychainTester.getSecretService();
-	}
-
-	@Override
-	public boolean isSupported() {
-		return SystemUtils.IS_OS_LINUX && LinuxKeychainTester.getSecretService().isPresent();
-	}
-
-	@Override
-	public void storePassphrase(String key, CharSequence passphrase) {
-		Preconditions.checkState(gnomeLoginKeyring.isPresent());
-		gnomeLoginKeyring.get().storePassword(key, passphrase);
-	}
-
-	@Override
-	public char[] loadPassphrase(String key) {
-		Preconditions.checkState(gnomeLoginKeyring.isPresent());
-		return gnomeLoginKeyring.get().loadPassword(key);
-	}
-
-	@Override
-	public void deletePassphrase(String key) {
-		Preconditions.checkState(gnomeLoginKeyring.isPresent());
-		gnomeLoginKeyring.get().deletePassword(key);
-	}
-}

+ 51 - 0
main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccess.java

@@ -0,0 +1,51 @@
+package org.cryptomator.keychain;
+
+import org.apache.commons.lang3.SystemUtils;
+
+import javax.inject.Inject;
+import java.util.Optional;
+
+/**
+ * A facade to LinuxSecretServiceKeychainAccessImpl that doesn't depend on libraries that are unavailable on Mac and Windows.
+ */
+public class LinuxSecretServiceKeychainAccess implements KeychainAccessStrategy {
+
+	// the actual implementation is hidden in this delegate object which is loaded via reflection,
+	// as it depends on libraries that aren't necessarily available:
+	private final Optional<KeychainAccessStrategy> delegate;
+
+	@Inject
+	public LinuxSecretServiceKeychainAccess() {
+		this.delegate = constructGnomeKeyringKeychainAccess();
+	}
+
+	private static Optional<KeychainAccessStrategy> constructGnomeKeyringKeychainAccess() {
+		try {
+			Class<?> clazz = Class.forName("org.cryptomator.keychain.LinuxSecretServiceKeychainAccessImpl");
+			KeychainAccessStrategy instance = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance();
+			return Optional.of(instance);
+		} catch (Exception e) {
+			return Optional.empty();
+		}
+	}
+
+	@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);
+	}
+}

+ 65 - 0
main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccessImpl.java

@@ -0,0 +1,65 @@
+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 {
+
+	@Override
+	public boolean isSupported() {
+		try (@SuppressWarnings("unused") SimpleCollection keyring = new SimpleCollection()) {
+			// seems like we're able to access the keyring.
+			return true;
+		} catch (IOException e) {
+			return false;
+		}
+	}
+
+	@Override
+	public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
+		try (SimpleCollection keyring = new SimpleCollection()) {
+			List<String> list = keyring.getItems(createAttributes(key));
+			if (list == null) {
+				keyring.createItem("Cryptomator", passphrase, createAttributes(key));
+			}
+		} catch (IOException e) {
+			throw new KeychainAccessException(e);
+		}
+	}
+
+	@Override
+	public char[] loadPassphrase(String key) throws KeychainAccessException {
+		try (SimpleCollection keyring = new SimpleCollection()) {
+			List<String> 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<String> list = keyring.getItems(createAttributes(key));
+			if (list != null) {
+				keyring.deleteItem(list.get(0));
+			}
+		} catch (IOException e) {
+			throw new KeychainAccessException(e);
+		}
+	}
+
+	private Map<String, String> createAttributes(String key) {
+		Map<String, String> attributes = new HashMap();
+		attributes.put("Vault", key);
+		return attributes;
+	}
+}

+ 1 - 1
main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java

@@ -13,7 +13,7 @@ import java.util.Optional;
 public class KeychainModuleTest {
 
 	@Test
-	public void testGetKeychain() {
+	public void testGetKeychain() throws KeychainAccessException {
 		Optional<KeychainAccess> keychainAccess = DaggerTestKeychainComponent.builder().keychainModule(new TestKeychainModule()).build().keychainAccess();
 		Assertions.assertTrue(keychainAccess.isPresent());
 		Assertions.assertTrue(keychainAccess.get() instanceof MapKeychainAccess);

+ 1 - 1
main/keychain/src/test/java/org/cryptomator/keychain/TestKeychainModule.java

@@ -10,7 +10,7 @@ import java.util.Set;
 public class TestKeychainModule extends KeychainModule {
 
 	@Override
-	Set<KeychainAccessStrategy> provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsProtectedKeychainAccess winKeychain, LinuxSecretServiceAccess linKeychain) {
+	Set<KeychainAccessStrategy> provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsProtectedKeychainAccess winKeychain, LinuxSecretServiceKeychainAccess linKeychain) {
 		return Set.of(new MapKeychainAccess());
 	}
 

+ 1 - 1
main/launcher/pom.xml

@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>org.cryptomator</groupId>
 		<artifactId>main</artifactId>
-		<version>1.4.13</version>
+		<version>1.4.14</version>
 	</parent>
 	<artifactId>launcher</artifactId>
 	<name>Cryptomator Launcher</name>

+ 1 - 1
main/pom.xml

@@ -3,7 +3,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>org.cryptomator</groupId>
 	<artifactId>main</artifactId>
-	<version>1.4.13</version>
+	<version>1.4.14</version>
 	<packaging>pom</packaging>
 	<name>Cryptomator</name>
 

+ 1 - 1
main/ui/pom.xml

@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>org.cryptomator</groupId>
 		<artifactId>main</artifactId>
-		<version>1.4.13</version>
+		<version>1.4.14</version>
 	</parent>
 	<artifactId>ui</artifactId>
 	<name>Cryptomator GUI</name>

+ 25 - 12
main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java

@@ -40,6 +40,7 @@ import org.cryptomator.common.settings.VolumeImpl;
 import org.cryptomator.cryptolib.api.InvalidPassphraseException;
 import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
 import org.cryptomator.keychain.KeychainAccess;
+import org.cryptomator.keychain.KeychainAccessException;
 import org.cryptomator.ui.controls.SecPasswordField;
 import org.cryptomator.ui.l10n.Localization;
 import org.cryptomator.ui.model.Vault;
@@ -215,12 +216,16 @@ public class UnlockController implements ViewController {
 		savePassword.setSelected(false);
 		// auto-fill pw from keychain:
 		if (keychainAccess.isPresent()) {
-			char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId());
-			if (storedPw != null) {
-				savePassword.setSelected(true);
-				passwordField.setPassword(storedPw);
-				passwordField.selectRange(storedPw.length, storedPw.length);
-				Arrays.fill(storedPw, ' ');
+			try {
+				char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId());
+				if (storedPw != null) {
+					savePassword.setSelected(true);
+					passwordField.setPassword(storedPw);
+					passwordField.selectRange(storedPw.length, storedPw.length);
+					Arrays.fill(storedPw, ' ');
+				}
+			} catch (KeychainAccessException e) {
+				LOG.error("Failed to load stored password from system keychain.", e);
 			}
 		}
 		VaultSettings vaultSettings = vault.getVaultSettings();
@@ -450,7 +455,11 @@ public class UnlockController implements ViewController {
 
 			Optional<ButtonType> choice = confirmDialog.showAndWait();
 			if (ButtonType.OK.equals(choice.get())) {
-				keychainAccess.get().deletePassphrase(vault.getId());
+				try {
+					keychainAccess.get().deletePassphrase(vault.getId());
+				} catch (KeychainAccessException e) {
+					LOG.error("Failed to remove entry from system keychain.", e);
+				}
 			} else if (ButtonType.CANCEL.equals(choice.get())) {
 				savePassword.setSelected(true);
 			}
@@ -458,12 +467,16 @@ public class UnlockController implements ViewController {
 	}
 
 	private boolean hasStoredPassword() {
-		char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId());
-		boolean hasPw = (storedPw != null);
-		if (storedPw != null) {
-			Arrays.fill(storedPw, ' ');
+		try {
+			char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId());
+			boolean hasPw = (storedPw != null);
+			if (storedPw != null) {
+				Arrays.fill(storedPw, ' ');
+			}
+			return hasPw;
+		} catch (KeychainAccessException e) {
+			return false;
 		}
-		return hasPw;
 	}
 
 	// ****************************************

+ 8 - 6
main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java

@@ -8,6 +8,7 @@ package org.cryptomator.ui.model;
 import org.cryptomator.common.FxApplicationScoped;
 import org.cryptomator.cryptolib.api.CryptoException;
 import org.cryptomator.keychain.KeychainAccess;
+import org.cryptomator.keychain.KeychainAccessException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -65,15 +66,16 @@ public class AutoUnlocker {
 	}
 
 	private void unlockSilently(Vault vault) {
-		char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId());
-		if (storedPw == null) {
-			LOG.warn("No passphrase stored in keychain for vault registered for auto unlocking: {}", vault.getPath());
-			return;
-		}
+		char[] storedPw = new char[0];
 		try {
+			storedPw = keychainAccess.get().loadPassphrase(vault.getId());
+			if (storedPw == null) {
+				LOG.warn("No passphrase stored in keychain for vault registered for auto unlocking: {}", vault.getPath());
+				return;
+			}
 			vault.unlock(CharBuffer.wrap(storedPw));
 			revealSilently(vault);
-		} catch (IOException | CryptoException | Volume.VolumeException e) {
+		} catch (IOException | CryptoException | Volume.VolumeException | KeychainAccessException e) {
 			LOG.error("Auto unlock failed.", e);
 		} finally {
 			Arrays.fill(storedPw, ' ');