diff --git a/pom.xml b/pom.xml
index 5a25d71..1c60f85 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,7 +44,7 @@
2.22.1
2.0.1-alpha
1.4.0
- 1.1.1
+ 1.2.0
1.1.1
1.4.2
diff --git a/src/main/java/org/cryptomator/linux/keychain/SecretServiceKeychainAccess.java b/src/main/java/org/cryptomator/linux/keychain/SecretServiceKeychainAccess.java
index 84938f4..cffe27c 100644
--- a/src/main/java/org/cryptomator/linux/keychain/SecretServiceKeychainAccess.java
+++ b/src/main/java/org/cryptomator/linux/keychain/SecretServiceKeychainAccess.java
@@ -7,18 +7,21 @@
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.freedesktop.dbus.DBusPath;
import org.purejava.secret.api.Collection;
+import org.purejava.secret.api.DBusMessageHandler;
import org.purejava.secret.api.EncryptedSession;
import org.purejava.secret.api.Item;
+import org.purejava.secret.api.Pair;
import org.purejava.secret.api.Static;
import org.purejava.secret.api.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import static org.purejava.secret.api.DBusMessageHandler.DBusResult.*;
+
@Priority(1100)
@OperatingSystem(OperatingSystem.Value.LINUX)
@DisplayName("Secret Service")
@@ -36,9 +39,15 @@ public SecretServiceKeychainAccess() {
session.getService().addCollectionCreatedHandler(collection -> LOG.debug("Collection {} created", collection.getPath()));
session.getService().addCollectionDeletedHandler(collection -> LOG.debug("Collection {} deleted", collection.getPath()));
var getAlias = session.getService().readAlias("default");
- if (getAlias.isSuccess() && "/".equals(getAlias.value().getPath())) {
- // default alias is not set; set it to the login keyring
- session.getService().setAlias("default", new DBusPath(Static.DBusPath.LOGIN_COLLECTION));
+ switch (getAlias) {
+ case Success success-> {
+ if ("/".equals(success.value().getPath())) {
+ // default alias is not set; set it to the login keyring
+ session.getService().setAlias("default", new DBusPath(Static.DBusPath.LOGIN_COLLECTION));
+ }
+ }
+ case Failure failure
+ -> LOG.warn("Getting the collection with the \"default\" alias failed with: {}", failure.error().getMessage());
}
collection.addItemChangedHandler(item -> LOG.debug("Item {} changed", item.getPath()));
collection.addItemCreatedHandler(item -> LOG.debug("Item {} created", item.getPath()));
@@ -50,28 +59,66 @@ public SecretServiceKeychainAccess() {
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
try {
var call = collection.searchItems(withKey(key));
- if (call.isSuccess()) {
- if (call.value().isEmpty()) {
- List lockable = new ArrayList<>();
- lockable.add(new DBusPath(collection.getDBusPath()));
- var promptNeededToUnlock = session.getService().unlock(lockable);
- if (promptNeededToUnlock.isSuccess() && !"/".equals(promptNeededToUnlock.value().b.getPath())) {
- Util.promptAndGetResultAsArrayList(promptNeededToUnlock.value().b);
+ switch (call) {
+ case DBusMessageHandler.DBusResult.Success> success
+ when success.value().isEmpty() -> {
+ List lockable = List.of(new DBusPath(collection.getDBusPath()));
+ var unlockResult = session.getService().unlock(lockable);
+
+ switch (unlockResult) {
+ case Success, DBusPath>> unlockSuccess -> {
+ /* The unlockResult variable may contain a prompt: when you call unlock for the collection
+ in order to be able to store the secret within the collection, depending on whether
+ a prompt is needed to unlock the collection or not, a prompt needs to be handled or not.*/
+ var prompt = unlockSuccess.value().b;
+ if (!"/".equals(prompt.getPath())) {
+ Util.promptAndGetResultAsArrayList(prompt);
+ }
+ }
+ case Failure, DBusPath>> unlockFailure ->
+ LOG.warn("Failed to unlock collection {}",
+ collection.getDBusPath(),
+ unlockFailure.error());
}
- var itemProps = Item.createProperties(LABEL_FOR_SECRET_IN_KEYRING, withKeyAndName(key, displayName));
+
+ var itemProps = Item.createProperties(
+ LABEL_FOR_SECRET_IN_KEYRING,
+ withKeyAndName(key, displayName)
+ );
var secret = session.encrypt(passphrase);
var created = collection.createItem(itemProps, secret, false);
- if (!created.isSuccess()) {
- throw new KeychainAccessException("Storing password failed", created.error());
+
+ switch (created) {
+ case Success> successful ->
+ LOG.debug("Created item {} on collection {}",
+ successful.value().a.getPath(),
+ collection.getDBusPath());
+ case Failure> failure ->
+ throw new KeychainAccessException(
+ "Storing password failed for collection "
+ + collection.getDBusPath(),
+ failure.error()
+ );
}
- } else {
- changePassphrase(key, displayName, passphrase);
}
- } else {
- throw new KeychainAccessException("Storing password failed", call.error());
+ case DBusMessageHandler.DBusResult.Success> _ ->
+ changePassphrase(key, displayName, passphrase);
+
+ case DBusMessageHandler.DBusResult.Failure> failure ->
+ throw new KeychainAccessException(
+ "Storing password failed for collection "
+ + collection.getDBusPath(),
+ failure.error()
+ );
}
+ } catch (KeychainAccessException e) {
+ throw e;
} catch (Exception e) {
- throw new KeychainAccessException("Storing password failed.", e);
+ throw new KeychainAccessException(
+ "Storing password failed for collection "
+ + collection.getDBusPath(),
+ e
+ );
}
}
@@ -79,20 +126,45 @@ public void storePassphrase(String key, String displayName, CharSequence passphr
public char[] loadPassphrase(String key) throws KeychainAccessException {
try {
var call = collection.searchItems(withKey(key));
- if (call.isSuccess()) {
- if (!call.value().isEmpty()) {
- var path = call.value().getFirst();
+
+ return switch (call) {
+
+ case Success> success
+ when success.value().isEmpty() ->
+ null;
+
+ case Success> success
+ when success.value().size() > 1 ->
+ throw new KeychainAccessException(
+ "Expected exactly one item, but found "
+ + success.value().size()
+ );
+
+ case Success> success -> {
+ var path = success.value().getFirst();
+
session.getService().ensureUnlocked(path);
+
var secret = new Item(path).getSecret(session.getSession());
- return session.decrypt(secret);
- } else {
- return null;
+ yield session.decrypt(secret);
}
- } else {
- throw new KeychainAccessException("Loading password failed", call.error());
- }
+
+ case Failure> failure ->
+ throw new KeychainAccessException(
+ "Loading password failed for collection "
+ + collection.getDBusPath(),
+ failure.error()
+ );
+ };
+
+ } catch (KeychainAccessException e) {
+ throw e;
} catch (Exception e) {
- throw new KeychainAccessException("Loading password failed.", e);
+ throw new KeychainAccessException(
+ "Loading password failed for collection "
+ + collection.getDBusPath(),
+ e
+ );
}
}
@@ -100,48 +172,135 @@ public char[] loadPassphrase(String key) throws KeychainAccessException {
public void deletePassphrase(String key) throws KeychainAccessException {
try {
var call = collection.searchItems(withKey(key));
- if (call.isSuccess()) {
- if (!call.value().isEmpty()) {
- var path = call.value().getFirst();
+
+ switch (call) {
+
+ case Success> success
+ when success.value().isEmpty() ->
+ LOG.debug("Deleting entry with {}={} failed: No such item found",
+ ID_KEY,
+ key);
+
+ case Success> success
+ when success.value().size() > 1 ->
+ throw new KeychainAccessException(
+ "Expected exactly one item, but found "
+ + success.value().size()
+ );
+
+ case Success> success -> {
+ var path = success.value().getFirst();
session.getService().ensureUnlocked(path);
var item = new Item(path);
- var deleted = item.delete();
- if (!deleted.isSuccess()) {
- throw new KeychainAccessException("Deleting password failed", deleted.error());
+
+ switch (item.delete()) {
+ case Success _ ->
+ LOG.debug("Deleted item {} from collection {}",
+ path.getPath(),
+ collection.getDBusPath());
+
+ case Failure failure -> {
+ LOG.warn("Failed to delete item {} from collection {}",
+ path.getPath(),
+ collection.getDBusPath(),
+ failure.error());
+
+ throw new KeychainAccessException(
+ "Deleting password failed for collection "
+ + collection.getDBusPath(),
+ failure.error()
+ );
+ }
}
- } else {
- LOG.debug("Deleting entry with {}={} failed: No such item found", ID_KEY, key);
}
- } else {
- throw new KeychainAccessException("Deleting password failed", call.error());
+
+ case Failure> failure ->
+ throw new KeychainAccessException(
+ "Deleting password failed for collection "
+ + collection.getDBusPath(),
+ failure.error()
+ );
}
+
+ } catch (KeychainAccessException e) {
+ throw e;
} catch (Exception e) {
- throw new KeychainAccessException("Deleting password failed", e);
+ throw new KeychainAccessException(
+ "Deleting password failed for collection "
+ + collection.getDBusPath(),
+ e
+ );
}
}
@Override
- public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
+ public void changePassphrase(String key, String displayName, CharSequence passphrase)
+ throws KeychainAccessException {
try {
var call = collection.searchItems(withKey(key));
- if (call.isSuccess()) {
- if (!call.value().isEmpty()) {
- session.getService().ensureUnlocked(call.value().getFirst());
+
+ switch (call) {
+
+ case Success> success
+ when success.value().isEmpty() ->
+ throw new KeychainAccessException(
+ "Vault " + key + " not found, updating failed"
+ );
+
+ case Success> success
+ when success.value().size() > 1 ->
+ throw new KeychainAccessException(
+ "Expected exactly one item, but found "
+ + success.value().size()
+ );
+
+ case Success> success -> {
+ var path = success.value().getFirst();
+ session.getService().ensureUnlocked(path);
var secret = session.encrypt(passphrase);
- var itemProps = Item.createProperties(LABEL_FOR_SECRET_IN_KEYRING, withKeyAndName(key, displayName));
+ var itemProps = Item.createProperties(
+ LABEL_FOR_SECRET_IN_KEYRING,
+ withKeyAndName(key, displayName)
+ );
var updated = collection.createItem(itemProps, secret, true);
- if (!updated.isSuccess()) {
- throw new KeychainAccessException("Updating password failed", updated.error());
+
+ switch (updated) {
+ case Success> _ ->
+ LOG.debug("Updated item {} in collection {}",
+ path.getPath(),
+ collection.getDBusPath());
+
+ case Failure> failure -> {
+ LOG.warn("Failed to update item {} in collection {}",
+ path.getPath(),
+ collection.getDBusPath(),
+ failure.error());
+
+ throw new KeychainAccessException(
+ "Updating password failed for collection "
+ + collection.getDBusPath(),
+ failure.error()
+ );
+ }
}
- } else {
- var msg = "Vault " + key + " not found, updating failed";
- throw new KeychainAccessException(msg);
}
- } else {
- throw new KeychainAccessException("Updating password failed", call.error());
+
+ case Failure> failure ->
+ throw new KeychainAccessException(
+ "Updating password failed for collection "
+ + collection.getDBusPath(),
+ failure.error()
+ );
}
+
+ } catch (KeychainAccessException e) {
+ throw e;
} catch (Exception e) {
- throw new KeychainAccessException("Updating password failed", e);
+ throw new KeychainAccessException(
+ "Updating password failed for collection "
+ + collection.getDBusPath(),
+ e
+ );
}
}
@@ -158,8 +317,19 @@ public boolean isSupported() {
@Override
public boolean isLocked() {
- var call = collection.isLocked();
- return !call.isSuccess() || call.value();
+ return switch (collection.isLocked()) {
+ case Success success ->
+ success.value(); // yields the value
+
+ case Failure failure -> {
+ LOG.warn(
+ "Failed to determine lock state of collection {}",
+ collection.getDBusPath(),
+ failure.error()
+ );
+ yield true;
+ }
+ };
}
private Map withKey(String key) {