mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 10:11:27 +00:00
Merge pull request #1294 from purejava/access-kde-wallets
This adds the missing part to linux system keychain access: use KDE wallets
This commit is contained in:
@@ -67,7 +67,7 @@
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||
<excludeClassifiers>linux,mac,win</excludeClassifiers>
|
||||
<excludeArtifactIds>dbus-java,secret-service,hkdf,java-utils</excludeArtifactIds>
|
||||
<excludeArtifactIds>dbus-java,secret-service,kdewallet,hkdf,java-utils</excludeArtifactIds>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
@@ -83,14 +83,14 @@
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-linux-secret-service</id>
|
||||
<id>copy-linux-system-keychain-access</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/linux-libs</outputDirectory>
|
||||
<includeArtifactIds>dbus-java,secret-service,hkdf,java-utils</includeArtifactIds>
|
||||
<includeArtifactIds>dbus-java,secret-service,kdewallet,hkdf,java-utils</includeArtifactIds>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-graphics</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Apache -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
@@ -53,6 +53,12 @@
|
||||
<artifactId>secret-service</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- kdewallet lib -->
|
||||
<dependency>
|
||||
<groupId>org.purejava</groupId>
|
||||
<artifactId>kdewallet</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
||||
@@ -28,7 +28,7 @@ public abstract class KeychainModule {
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract KeychainAccessStrategy bindLinuxSecretServiceKeychainAccess(LinuxSecretServiceKeychainAccess keychainAccessStrategy);
|
||||
abstract KeychainAccessStrategy bindLinuxSystemKeychainAccess(LinuxSystemKeychainAccess keychainAccessStrategy);
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
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 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() {
|
||||
try {
|
||||
connection = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION);
|
||||
} catch (DBusException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
try {
|
||||
wallet = new KDEWallet(connection);
|
||||
return wallet.isEnabled();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
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(e.toString(), e.getCause());
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
throw new KeychainAccessException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,24 +7,28 @@ import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A facade to LinuxSecretServiceKeychainAccessImpl that doesn't depend on libraries that are unavailable on Mac and Windows.
|
||||
* A facade to LinuxSecretServiceKeychainAccessImpl and LinuxKDEWalletKeychainAccessImpl
|
||||
* that depend on libraries that are unavailable on Mac and Windows.
|
||||
*/
|
||||
@Singleton
|
||||
public class LinuxSecretServiceKeychainAccess implements KeychainAccessStrategy {
|
||||
public class LinuxSystemKeychainAccess implements KeychainAccessStrategy {
|
||||
|
||||
// the actual implementation is hidden in this delegate object which is loaded via reflection,
|
||||
// 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<KeychainAccessStrategy> delegate;
|
||||
|
||||
@Inject
|
||||
public LinuxSecretServiceKeychainAccess() {
|
||||
this.delegate = constructGnomeKeyringKeychainAccess();
|
||||
public LinuxSystemKeychainAccess() {
|
||||
this.delegate = constructKeychainAccess();
|
||||
}
|
||||
|
||||
private static Optional<KeychainAccessStrategy> constructGnomeKeyringKeychainAccess() {
|
||||
try {
|
||||
private static Optional<KeychainAccessStrategy> constructKeychainAccess() {
|
||||
try { // is gnome-keyring or kwallet installed?
|
||||
Class<?> clazz = Class.forName("org.cryptomator.keychain.LinuxSecretServiceKeychainAccessImpl");
|
||||
KeychainAccessStrategy instance = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance();
|
||||
if (instance.isSupported()) return Optional.of(instance);
|
||||
clazz = Class.forName("org.cryptomator.keychain.LinuxKDEWalletKeychainAccessImpl");
|
||||
instance = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance();
|
||||
return Optional.of(instance);
|
||||
} catch (Exception e) {
|
||||
return Optional.empty();
|
||||
@@ -34,6 +34,7 @@
|
||||
<javafx.version>14</javafx.version>
|
||||
<commons-lang3.version>3.11</commons-lang3.version>
|
||||
<secret-service.version>1.1.0</secret-service.version>
|
||||
<kdewallet.version>1.0.1</kdewallet.version>
|
||||
<jwt.version>3.10.3</jwt.version>
|
||||
<easybind.version>2.1.0</easybind.version>
|
||||
<guava.version>29.0-jre</guava.version>
|
||||
@@ -168,6 +169,11 @@
|
||||
<artifactId>secret-service</artifactId>
|
||||
<version>${secret-service.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.purejava</groupId>
|
||||
<artifactId>kdewallet</artifactId>
|
||||
<version>${kdewallet.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
|
||||
Reference in New Issue
Block a user