From 5d85e9bde5cb8ac16dc978c602bf488a957a38dd Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Thu, 21 Oct 2021 14:47:38 +0200
Subject: [PATCH 01/74] Add versions-maven-plugin for dependency updates
---
pom.xml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/pom.xml b/pom.xml
index 83ba256..f450864 100644
--- a/pom.xml
+++ b/pom.xml
@@ -165,6 +165,7 @@
+
@@ -238,6 +239,11 @@
jacoco-maven-plugin
0.8.10
+
+ org.codehaus.mojo
+ versions-maven-plugin
+ 2.8.1
+
@@ -328,6 +334,17 @@
+
+ org.codehaus.mojo
+ versions-maven-plugin
+
+ false
+ false
+ true
+
+ true
+
+
From cf74249add140ae70a781480956d14e5c1f9e0ef Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sun, 20 Feb 2022 20:13:00 +0100
Subject: [PATCH 02/74] Require JDK17 and update dbus-java to version `4.0.0`
and
---
pom.xml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index f450864..dcf743b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,7 +76,8 @@
${hkdf.version}
-
+
+
com.github.hypfvieh
dbus-java-core
From 451ff3eb3e05e28d038b0b6ff8a5542fbba5ed96 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sun, 6 Mar 2022 13:42:27 +0100
Subject: [PATCH 03/74] Add Optional<>
---
pom.xml | 6 ++
.../handlers/MessageHandler.java | 59 +++++++++++--------
.../secretservice/handlers/Messaging.java | 13 ++--
.../secretservice/handlers/SignalHandler.java | 5 +-
4 files changed, 51 insertions(+), 32 deletions(-)
diff --git a/pom.xml b/pom.xml
index dcf743b..af7f0eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -134,6 +134,12 @@
test
+
+ org.apache.commons
+ commons-lang3
+ 3.12.0
+
+
diff --git a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
index 1e46ac9..2e39495 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
@@ -1,5 +1,6 @@
package de.swiesend.secretservice.handlers;
+import org.apache.commons.lang3.ArrayUtils;
import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.messages.MethodCall;
@@ -9,6 +10,7 @@
import org.slf4j.LoggerFactory;
import java.util.Arrays;
+import java.util.Optional;
import static de.swiesend.secretservice.Static.DBus.MAX_DELAY_MILLIS;
@@ -22,7 +24,7 @@ public MessageHandler(DBusConnection connection) {
this.connection = connection;
}
- public Object[] send(String service, String path, String iface, String method, String signature, Object... args) {
+ public Optional send(String service, String path, String iface, String method, String signature, Object... args) {
try {
org.freedesktop.dbus.messages.Message message = new MethodCall(
service,
@@ -46,53 +48,60 @@ public Object[] send(String service, String path, String iface, String method, S
switch (error) {
case "org.freedesktop.Secret.Error.NoSession":
case "org.freedesktop.Secret.Error.NoSuchObject":
- log.warn(error + ": " + parameters[0]);
- return null;
+ if (ArrayUtils.isEmpty(parameters)) {
+ log.warn(error);
+ } else {
+ log.warn(error + ": " + parameters[0]);
+ }
+ return Optional.empty();
case "org.gnome.keyring.Error.Denied":
case "org.freedesktop.Secret.Error.IsLocked":
- log.info(error + ": " + parameters[0]);
- return null;
+ if (ArrayUtils.isEmpty(parameters)) {
+ log.info(error);
+ } else {
+ log.info(error + ": " + parameters[0]);
+ }
+ return Optional.empty();
case "org.freedesktop.DBus.Error.NoReply":
case "org.freedesktop.DBus.Error.UnknownMethod":
case "org.freedesktop.DBus.Error.ServiceUnknown":
case "org.freedesktop.dbus.exceptions.NotConnected":
case "org.freedesktop.DBus.Local.Disconnected":
case "org.freedesktop.dbus.exceptions.FatalDBusException":
- log.debug(error);
- return null;
+ if (log.isDebugEnabled()) log.debug(error);
+ return Optional.empty();
default:
- throw new DBusException(error);
+ log.error(error);
+ return Optional.empty();
}
}
- return parameters;
+ return Optional.ofNullable(parameters);
} catch (DBusException e) {
log.error("Unexpected D-Bus response:", e);
}
- return null;
+ return Optional.empty();
}
- public Variant getProperty(String service, String path, String iface, String property) {
- Object[] response = send(service, path, Static.DBus.Interfaces.DBUS_PROPERTIES,
+ public Optional getProperty(String service, String path, String iface, String property) {
+ Optional maybeResponse = send(service, path, Static.DBus.Interfaces.DBUS_PROPERTIES,
"Get", "ss", iface, property);
- if (response == null) return null;
- return (Variant) response[0];
+ if (!maybeResponse.isPresent()) return Optional.empty();
+ Object[] response = maybeResponse.get();
+ return ArrayUtils.isEmpty(response) ? Optional.empty() : Optional.ofNullable((Variant) response[0]);
}
- public Variant getAllProperties(String service, String path, String iface) {
- Object[] response = send(service, path, Static.DBus.Interfaces.DBUS_PROPERTIES,
+ public Optional getAllProperties(String service, String path, String iface) {
+ Optional maybeResponse = send(service, path, Static.DBus.Interfaces.DBUS_PROPERTIES,
"GetAll", "ss", iface);
- if (response == null) return null;
- return (Variant) response[0];
+ if (!maybeResponse.isPresent()) return Optional.empty();
+ Object[] response = maybeResponse.get();
+ return ArrayUtils.isEmpty(response) ? Optional.empty() : Optional.ofNullable((Variant) response[0]);
}
- public void setProperty(String service, String path, String iface, String property, Variant value) {
- send(service, path, Static.DBus.Interfaces.DBUS_PROPERTIES,
- "Set", "ssv", iface, property, value);
- }
-
- public DBusConnection getConnection() {
- return connection;
+ public boolean setProperty(String service, String path, String iface, String property, Variant value) {
+ Optional maybeResponse = send(service, path, Static.DBus.Interfaces.DBUS_PROPERTIES, "Set", "ssv", iface, property, value);
+ return maybeResponse.isPresent();
}
}
diff --git a/src/main/java/de/swiesend/secretservice/handlers/Messaging.java b/src/main/java/de/swiesend/secretservice/handlers/Messaging.java
index 2b316d0..1cf5fb9 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/Messaging.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/Messaging.java
@@ -7,6 +7,7 @@
import de.swiesend.secretservice.Static;
import java.util.List;
+import java.util.Optional;
public abstract class Messaging {
@@ -29,24 +30,24 @@ public Messaging(DBusConnection connection, List> si
this.interfaceName = interfaceName;
}
- protected Object[] send(String method) {
+ protected Optional send(String method) {
return msg.send(serviceName, objectPath, interfaceName, method, "");
}
- protected Object[] send(String method, String signature, Object... arguments) {
+ protected Optional send(String method, String signature, Object... arguments) {
return msg.send(serviceName, objectPath, interfaceName, method, signature, arguments);
}
- protected Variant getProperty(String property) {
+ protected Optional getProperty(String property) {
return msg.getProperty(serviceName, objectPath, interfaceName, property);
}
- protected Variant getAllProperties() {
+ protected Optional getAllProperties() {
return msg.getAllProperties(serviceName, objectPath, interfaceName);
}
- protected void setProperty(String property, Variant value) {
- msg.setProperty(serviceName, objectPath, interfaceName, property, value);
+ protected boolean setProperty(String property, Variant value) {
+ return msg.setProperty(serviceName, objectPath, interfaceName, property, value);
}
public String getServiceName() {
diff --git a/src/main/java/de/swiesend/secretservice/handlers/SignalHandler.java b/src/main/java/de/swiesend/secretservice/handlers/SignalHandler.java
index f402c8d..93055ce 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/SignalHandler.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/SignalHandler.java
@@ -46,8 +46,11 @@ public void connect(DBusConnection connection, List>
}
}
} catch (DBusException e) {
- log.error("Could not connect to the D-Bus.", e);
+ log.error("Could not connect to the D-Bus: ", e);
+ } catch (ClassCastException e) {
+ log.error("Could not cast a signal: ", e);
}
+
}
}
From ed834ff85fd441edd3a858313d322eef1c240d72 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sun, 6 Mar 2022 13:49:37 +0100
Subject: [PATCH 04/74] Add better error handling
---
.../de/swiesend/secretservice/handlers/MessageHandler.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
index 2e39495..17e9d37 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
@@ -71,14 +71,16 @@ public Optional send(String service, String path, String iface, String
if (log.isDebugEnabled()) log.debug(error);
return Optional.empty();
default:
- log.error(error);
+ log.error("Unexpected org.freedesktop.dbus.errors.Error: ", error);
return Optional.empty();
}
}
return Optional.ofNullable(parameters);
} catch (DBusException e) {
- log.error("Unexpected D-Bus response:", e);
+ log.error("Unexpected D-Bus response: ", e);
+ } catch (RuntimeException e) {
+ log.error("Unexpected: ", e);
}
return Optional.empty();
}
From 915445d9351fede3ac59424de01de9fd0ee98877 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sat, 2 Apr 2022 15:06:15 +0200
Subject: [PATCH 05/74] Let `MessageHandler` return `Optional`s
---
.../de/swiesend/secretservice/Collection.java | 72 +++---
.../java/de/swiesend/secretservice/Item.java | 77 ++++---
.../de/swiesend/secretservice/Prompt.java | 17 +-
.../de/swiesend/secretservice/Service.java | 102 +++++----
.../de/swiesend/secretservice/Session.java | 4 +-
.../de/swiesend/secretservice/Static.java | 31 ++-
.../secretservice/TransportEncryption.java | 7 +-
...ternalUnsupportedGuiltRiddenInterface.java | 31 ++-
...ternalUnsupportedGuiltRiddenInterface.java | 9 +-
.../handlers/MessageHandler.java | 28 ++-
.../secretservice/interfaces/Collection.java | 207 +++++++++---------
.../secretservice/interfaces/Item.java | 30 +--
.../secretservice/interfaces/Prompt.java | 9 +-
.../secretservice/interfaces/Service.java | 67 +++---
.../secretservice/interfaces/Session.java | 4 +-
.../simple/SimpleCollection.java | 50 +++--
.../integration/CollectionTest.java | 36 +--
.../integration/IntegrationTest.java | 8 +-
.../secretservice/integration/ItemTest.java | 32 +--
.../secretservice/integration/PromptTest.java | 6 +-
.../integration/ServiceTest.java | 79 ++++---
.../secretservice/integration/StaticTest.java | 26 ++-
...alUnsupportedGuiltRiddenInterfaceTest.java | 8 +-
.../integration/test/Context.java | 13 +-
24 files changed, 502 insertions(+), 451 deletions(-)
diff --git a/src/main/java/de/swiesend/secretservice/Collection.java b/src/main/java/de/swiesend/secretservice/Collection.java
index 8afefa5..338f95e 100644
--- a/src/main/java/de/swiesend/secretservice/Collection.java
+++ b/src/main/java/de/swiesend/secretservice/Collection.java
@@ -5,16 +5,16 @@
import org.freedesktop.dbus.messages.DBusSignal;
import org.freedesktop.dbus.types.UInt64;
import org.freedesktop.dbus.types.Variant;
+import de.swiesend.secretservice.Static.Utils;
import de.swiesend.secretservice.handlers.Messaging;
import java.util.*;
public class Collection extends Messaging implements de.swiesend.secretservice.interfaces.Collection {
- private String id;
-
public static final List> signals = Arrays.asList(
ItemCreated.class, ItemChanged.class, ItemDeleted.class);
+ private String id;
public Collection(DBusPath path, Service service) {
super(service.getConnection(), signals,
@@ -70,71 +70,69 @@ public Collection(String id, Service service) {
*/
public static Map createProperties(String label) {
HashMap properties = new HashMap();
- properties.put(LABEL, new Variant(label));
+ properties.put(LABEL, new Variant<>(label));
return properties;
}
@Override
- public ObjectPath delete() {
- Object[] response = send("Delete", "");
- if (response == null) return null;
- ObjectPath prompt = (ObjectPath) response[0];
+ public Optional delete() {
+ Optional prompt = send("Delete", "").flatMap(response ->
+ Utils.isNullOrEmpty(response) ? Optional.empty() : Optional.of((ObjectPath) response[0])
+ );
return prompt;
}
@Override
- public List searchItems(Map attributes) {
- Object[] response = send("SearchItems", "a{ss}", attributes);
- if (response == null) return null;
- return (List) response[0];
+ public Optional> searchItems(Map attributes) {
+ return send("SearchItems", "a{ss}", attributes).flatMap(response ->
+ Utils.isNullOrEmpty(response) ? Optional.empty() : Optional.of((List) response[0])
+ );
}
@Override
- public Pair createItem(Map properties, Secret secret,
- boolean replace) {
- Object[] response = send("CreateItem", "a{sv}(oayays)b", properties, secret, replace);
- if (response == null) return null;
- return new Pair(response[0], response[1]);
+ public Optional> createItem(Map properties, Secret secret, boolean replace) {
+ return send("CreateItem", "a{sv}(oayays)b", properties, secret, replace).flatMap(response ->
+ (Utils.isNullOrEmpty(response) || response.length != 2) ?
+ Optional.empty() :
+ Optional.of(new Pair((ObjectPath) response[0], (ObjectPath) response[1])));
}
@Override
- public List getItems() {
- Variant response = getProperty("Items");
- if (response == null) return null;
- return (ArrayList) response.getValue();
+ public Optional> getItems() {
+ return getProperty("Items").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((List) variant.getValue())
+ );
}
@Override
- public String getLabel() {
- Variant response = getProperty("Label");
- if (response == null) return null;
- return (String) response.getValue();
+ public Optional getLabel() {
+ return getProperty("Label").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((String) variant.getValue())
+ );
}
@Override
- public void setLabel(String label) {
- setProperty("Label", new Variant(label));
+ public boolean setLabel(String label) {
+ return setProperty("Label", new Variant(label));
}
@Override
public boolean isLocked() {
- Variant response = getProperty("Locked");
- if (response == null) return true;
- return (boolean) response.getValue();
+ Optional response = getProperty("Locked").map(variant ->
+ variant == null ? false : (boolean) variant.getValue());
+ return response.isPresent() ? response.get() : false;
}
@Override
- public UInt64 created() {
- Variant response = getProperty("Created");
- if (response == null) return null;
- return (UInt64) response.getValue();
+ public Optional created() {
+ return getProperty("Created").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((UInt64) variant.getValue()));
}
@Override
- public UInt64 modified() {
- Variant response = getProperty("Modified");
- if (response == null) return null;
- return (UInt64) response.getValue();
+ public Optional modified() {
+ return getProperty("Modified").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((UInt64) variant.getValue()));
}
@Override
diff --git a/src/main/java/de/swiesend/secretservice/Item.java b/src/main/java/de/swiesend/secretservice/Item.java
index 23be93a..26d04bc 100644
--- a/src/main/java/de/swiesend/secretservice/Item.java
+++ b/src/main/java/de/swiesend/secretservice/Item.java
@@ -82,17 +82,21 @@ public static Map createProperties(String label, Map delete() {
+ Object[] response = send("Delete", "").orElse(null);
+ if (Static.Utils.isNullOrEmpty(response)) return Optional.empty();
+ try {
+ ObjectPath prompt = (ObjectPath) response[0];
+ return Optional.of(prompt);
+ } catch (ClassCastException e) {
+ return Optional.empty();
+ }
}
@Override
- public Secret getSecret(ObjectPath session) {
- Object[] response = send("GetSecret", "o", session);
- if (response == null) return null;
+ public Optional getSecret(ObjectPath session) {
+ Object[] response = send("GetSecret", "o", session).orElse(null);
+ if (Static.Utils.isNullOrEmpty(response)) return Optional.empty();
try {
Object[] inner = (Object[]) response[0];
@@ -110,67 +114,62 @@ public Secret getSecret(ObjectPath session) {
secret = new Secret(session_path, parameters, value, contentType);
}
- return secret;
+ return Optional.of(secret);
} catch (ClassCastException | IndexOutOfBoundsException e) {
- return null;
+ return Optional.empty();
}
}
@Override
- public void setSecret(Secret secret) {
- send("SetSecret", "(oayays)", secret);
+ public boolean setSecret(Secret secret) {
+ return send("SetSecret", "(oayays)", secret).isPresent();
}
@Override
public boolean isLocked() {
- Variant response = getProperty("Locked");
- if (response == null) return true;
- return (boolean) response.getValue();
+ Optional response = getProperty("Locked").map(variant ->
+ variant == null ? false : (boolean) variant.getValue());
+ return response.isPresent() ? response.get() : false;
}
@Override
- public Map getAttributes() {
- Variant response = getProperty("Attributes");
- if (response == null) return null;
- return (Map) response.getValue();
+ public Optional> getAttributes() {
+ return getProperty("Attributes").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((Map) variant.getValue()));
}
@Override
- public void setAttributes(Map attributes) {
- setProperty("Attributes", new Variant(attributes, "a{ss}"));
+ public boolean setAttributes(Map attributes) {
+ return setProperty("Attributes", new Variant(attributes, "a{ss}"));
}
@Override
- public String getLabel() {
- Variant response = getProperty("Label");
- if (response == null) return null;
- return (String) response.getValue();
+ public Optional getLabel() {
+ return getProperty("Label").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((String) variant.getValue()));
}
@Override
- public void setLabel(String label) {
- setProperty("Label", new Variant(label));
+ public boolean setLabel(String label) {
+ return setProperty("Label", new Variant(label));
}
@Override
- public String getType() {
- Variant response = getProperty("Type");
- if (response == null) return null;
- return (String) response.getValue();
+ public Optional getType() {
+ return getProperty("Type").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((String) variant.getValue()));
}
@Override
- public UInt64 created() {
- Variant response = getProperty("Created");
- if (response == null) return null;
- return (UInt64) response.getValue();
+ public Optional created() {
+ return getProperty("Created").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((UInt64) variant.getValue()));
}
@Override
- public UInt64 modified() {
- Variant response = getProperty("Modified");
- if (response == null) return null;
- return (UInt64) response.getValue();
+ public Optional modified() {
+ return getProperty("Modified").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((UInt64) variant.getValue()));
}
@Override
diff --git a/src/main/java/de/swiesend/secretservice/Prompt.java b/src/main/java/de/swiesend/secretservice/Prompt.java
index 5676df2..da79f38 100644
--- a/src/main/java/de/swiesend/secretservice/Prompt.java
+++ b/src/main/java/de/swiesend/secretservice/Prompt.java
@@ -8,6 +8,7 @@
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import static de.swiesend.secretservice.Static.DEFAULT_PROMPT_TIMEOUT;
import static de.swiesend.secretservice.Static.ObjectPaths.PROMPT;
@@ -24,13 +25,13 @@ public Prompt(Service service) {
}
@Override
- public void prompt(String window_id) {
+ public boolean prompt(String window_id) {
objectPath = Static.ObjectPaths.prompt(window_id);
- send("Prompt", "s", window_id);
+ return send("Prompt", "s", window_id).isPresent();
}
@Override
- public void prompt(ObjectPath prompt) throws NoSuchObject {
+ public boolean prompt(ObjectPath prompt) {
objectPath = prompt.getPath();
String windowID = "";
@@ -41,10 +42,11 @@ public void prompt(ObjectPath prompt) throws NoSuchObject {
windowID = split[split.length - 1];
}
} catch (IndexOutOfBoundsException | NullPointerException e) {
- throw new NoSuchObject(objectPath);
+ // TODO: check if it is possible to can call the prompt anyway
+ return false;
}
- send("Prompt", "s", windowID);
+ return send("Prompt", "s", windowID).isPresent();
}
/**
@@ -82,10 +84,9 @@ public Completed await(ObjectPath path) {
return await(path, DEFAULT_PROMPT_TIMEOUT);
}
-
@Override
- public void dismiss() {
- send("Dismiss", "");
+ public boolean dismiss() {
+ return send("Dismiss", "").isPresent();
}
@Override
diff --git a/src/main/java/de/swiesend/secretservice/Service.java b/src/main/java/de/swiesend/secretservice/Service.java
index b02e906..903dc5b 100644
--- a/src/main/java/de/swiesend/secretservice/Service.java
+++ b/src/main/java/de/swiesend/secretservice/Service.java
@@ -6,15 +6,13 @@
import org.freedesktop.dbus.types.Variant;
import de.swiesend.secretservice.handlers.Messaging;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
public class Service extends Messaging implements de.swiesend.secretservice.interfaces.Service {
public static final List> signals = Arrays.asList(
CollectionCreated.class, CollectionChanged.class, CollectionDeleted.class);
+ private static final Logger log = LoggerFactory.getLogger(Service.class);
private Session session = null;
public Service(DBusConnection connection) {
@@ -25,88 +23,88 @@ public Service(DBusConnection connection) {
}
@Override
- public Pair, ObjectPath> openSession(String algorithm, Variant input) {
- Object[] params = send("OpenSession", "sv", algorithm, input);
- if (params == null) return null;
- session = new Session((ObjectPath) params[1], this);
- return new Pair(params[0], params[1]);
+ public Optional, ObjectPath>> openSession(String algorithm, Variant input) {
+ return send("OpenSession", "sv", algorithm, input)
+ .filter(response -> !Static.Utils.isNullOrEmpty(response) && response.length == 2)
+ .flatMap(response -> Optional.of(new Pair, ObjectPath>((Variant) response[0], (ObjectPath) response[1])))
+ .map(pair -> {
+ log.debug("Got session: " + pair.b.getPath());
+ session = new Session(pair.b, this);
+ return pair;
+ });
}
@Override
- public Pair createCollection(Map properties, String alias) {
- String a;
- if (alias == null) {
- a = "";
- } else {
- a = alias;
- }
- Object[] params = send("CreateCollection", "a{sv}s", properties, a);
- if (params == null) return null;
- return new Pair(params[0], params[1]);
+ public Optional> createCollection(Map properties, String alias) {
+ String a = alias == null ? "" : alias;
+ return send("CreateCollection", "a{sv}s", properties, a).flatMap(response ->
+ (Static.Utils.isNullOrEmpty(response) || response.length != 2) ?
+ Optional.empty() :
+ Optional.of(new Pair((ObjectPath) response[0], (ObjectPath) response[1])));
}
@Override
- public Pair createCollection(Map properties) {
+ public Optional> createCollection(Map properties) {
return createCollection(properties, "");
}
@Override
- public Pair, List> searchItems(Map attributes) {
- Object[] params = send("SearchItems", "a{ss}", attributes);
- if (params == null) return null;
- return new Pair(params[0], params[1]);
+ public Optional, List>> searchItems(Map attributes) {
+ return send("SearchItems", "a{ss}", attributes).flatMap(response ->
+ (Static.Utils.isNullOrEmpty(response) || response.length != 2) ?
+ Optional.empty() :
+ Optional.of(new Pair, List>((List) response[0], (List) response[1])));
}
@Override
- public Pair, ObjectPath> unlock(List objects) {
- Object[] params = send("Unlock", "ao", objects);
- if (params == null) return null;
- return new Pair(params[0], params[1]);
+ public Optional, ObjectPath>> unlock(List objects) {
+ return send("Unlock", "ao", objects).flatMap(response ->
+ (Static.Utils.isNullOrEmpty(response) || response.length != 2) ?
+ Optional.empty() :
+ Optional.of(new Pair, ObjectPath>((List) response[0], (ObjectPath) response[1])));
}
@Override
- public Pair, ObjectPath> lock(List objects) {
- Object[] params = send("Lock", "ao", objects);
- if (params == null) return null;
- return new Pair(params[0], params[1]);
+ public Optional, ObjectPath>> lock(List objects) {
+ return send("Lock", "ao", objects).flatMap(response ->
+ (Static.Utils.isNullOrEmpty(response) || response.length != 2) ?
+ Optional.empty() :
+ Optional.of(new Pair, ObjectPath>((List) response[0], (ObjectPath) response[1])));
}
@Override
- public void lockService() {
- send("LockService", "");
+ public boolean lockService() {
+ return send("LockService", "").isPresent();
}
@Override
- public ObjectPath changeLock(ObjectPath collection) {
- Object[] params = send("ChangeLock", "o", collection);
- if (params == null) return null;
- return (ObjectPath) params[0];
+ public Optional changeLock(ObjectPath collection) {
+ return send("ChangeLock", "o", collection).flatMap(response ->
+ Static.Utils.isNullOrEmpty(response) ? Optional.empty() : Optional.of((ObjectPath) response[0]));
}
@Override
- public Map getSecrets(List items, ObjectPath session) {
- Object[] params = send("GetSecrets", "aoo", items, session);
- if (params == null) return null;
- return (Map) params[0];
+ public Optional> getSecrets(List items, ObjectPath session) {
+ return send("GetSecrets", "aoo", items, session).flatMap(response ->
+ Static.Utils.isNullOrEmpty(response) ? Optional.empty() : Optional.of((Map) response[0]));
}
@Override
- public ObjectPath readAlias(String name) {
- Object[] params = send("ReadAlias", "s", name);
- if (params == null) return null;
- return (ObjectPath) params[0];
+ public Optional readAlias(String name) {
+ return send("ReadAlias", "s", name).flatMap(response ->
+ Static.Utils.isNullOrEmpty(response) ? Optional.empty() : Optional.of((ObjectPath) response[0]));
}
@Override
- public void setAlias(String name, ObjectPath collection) {
- send("SetAlias", "so", name, collection);
+ public boolean setAlias(String name, ObjectPath collection) {
+ return send("SetAlias", "so", name, collection).isPresent();
}
@Override
- public List getCollections() {
- Variant response = getProperty("Collections");
- if (response == null) return null;
- return (ArrayList) response.getValue();
+ public Optional> getCollections() {
+ return getProperty("Collections").flatMap(variant ->
+ variant == null ? Optional.empty() : Optional.ofNullable((ArrayList) variant.getValue())
+ );
}
@Override
diff --git a/src/main/java/de/swiesend/secretservice/Session.java b/src/main/java/de/swiesend/secretservice/Session.java
index ea23398..a42e461 100644
--- a/src/main/java/de/swiesend/secretservice/Session.java
+++ b/src/main/java/de/swiesend/secretservice/Session.java
@@ -18,8 +18,8 @@ public Session(String session_id, Service service) {
}
@Override
- public void close() {
- send("Close");
+ public boolean close() {
+ return send("Close").isPresent();
}
@Override
diff --git a/src/main/java/de/swiesend/secretservice/Static.java b/src/main/java/de/swiesend/secretservice/Static.java
index 1e26b4c..8fdd906 100644
--- a/src/main/java/de/swiesend/secretservice/Static.java
+++ b/src/main/java/de/swiesend/secretservice/Static.java
@@ -7,19 +7,12 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
public class Static {
public static final Duration DEFAULT_PROMPT_TIMEOUT = Duration.ofSeconds(120);
- public static boolean isNullOrEmpty(final CharSequence cs) {
- return cs == null || cs.toString().trim().isEmpty();
- }
-
- public static boolean isNullOrEmpty(final String s) {
- return s == null || s.trim().isEmpty();
- }
-
public static class DBus {
public static final Long DEFAULT_DELAY_MILLIS = 100L;
@@ -181,6 +174,14 @@ public static ObjectPath toObjectPath(String path) {
return new ObjectPath("", path);
}
+ public static Optional toObjectPath(Object obj) {
+ try {
+ return Optional.of((ObjectPath) obj);
+ } catch (ClassCastException e) {
+ return Optional.empty();
+ }
+ }
+
public static List toStrings(List paths) {
ArrayList ps = new ArrayList();
for (ObjectPath p : paths) {
@@ -199,6 +200,20 @@ public static List toDBusPaths(List paths) {
}
+ public static class Utils {
+ public static boolean isNullOrEmpty(final CharSequence cs) {
+ return cs == null || cs.toString().trim().isEmpty();
+ }
+
+ public static boolean isNullOrEmpty(final String s) {
+ return s == null || s.trim().isEmpty();
+ }
+
+ public static boolean isNullOrEmpty(Object[] objects) {
+ return objects == null || objects.length == 0;
+ }
+ }
+
/**
* RFC 2409: https://tools.ietf.org/html/rfc2409
*/
diff --git a/src/main/java/de/swiesend/secretservice/TransportEncryption.java b/src/main/java/de/swiesend/secretservice/TransportEncryption.java
index 8c358c9..388c46b 100644
--- a/src/main/java/de/swiesend/secretservice/TransportEncryption.java
+++ b/src/main/java/de/swiesend/secretservice/TransportEncryption.java
@@ -19,6 +19,7 @@
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
+import java.util.Optional;
public class TransportEncryption implements AutoCloseable {
@@ -78,12 +79,12 @@ public boolean openSession() throws DBusException {
BigInteger ya = ((DHPublicKey) publicKey).getY();
// open session with "Client DH pub key as an array of bytes" without prime or generator
- Pair, ObjectPath> osResponse = service.openSession(
+ Optional, ObjectPath>> osResponse = service.openSession(
Static.Algorithm.DH_IETF1024_SHA256_AES128_CBC_PKCS7, new Variant(ya.toByteArray()));
// transform peer's raw Y to a public key
- if (osResponse != null) {
- yb = osResponse.a.getValue();
+ if (osResponse.isPresent()) {
+ yb = osResponse.get().a.getValue();
return true;
} else {
return false;
diff --git a/src/main/java/de/swiesend/secretservice/gnome/keyring/InternalUnsupportedGuiltRiddenInterface.java b/src/main/java/de/swiesend/secretservice/gnome/keyring/InternalUnsupportedGuiltRiddenInterface.java
index 4a797ce..3234933 100644
--- a/src/main/java/de/swiesend/secretservice/gnome/keyring/InternalUnsupportedGuiltRiddenInterface.java
+++ b/src/main/java/de/swiesend/secretservice/gnome/keyring/InternalUnsupportedGuiltRiddenInterface.java
@@ -1,14 +1,15 @@
package de.swiesend.secretservice.gnome.keyring;
-import de.swiesend.secretservice.Static;
import org.freedesktop.dbus.DBusPath;
import org.freedesktop.dbus.ObjectPath;
import org.freedesktop.dbus.types.Variant;
import de.swiesend.secretservice.Secret;
import de.swiesend.secretservice.Service;
+import de.swiesend.secretservice.Static;
import de.swiesend.secretservice.handlers.Messaging;
import java.util.Map;
+import java.util.Optional;
public class InternalUnsupportedGuiltRiddenInterface extends Messaging implements
de.swiesend.secretservice.gnome.keyring.interfaces.InternalUnsupportedGuiltRiddenInterface {
@@ -21,25 +22,33 @@ public InternalUnsupportedGuiltRiddenInterface(Service service) {
}
@Override
- public void changeWithMasterPassword(DBusPath collection, Secret original, Secret master) {
- send("ChangeWithMasterPassword", "o(oayays)(oayays)", collection, original, master);
+ public boolean changeWithMasterPassword(DBusPath collection, Secret original, Secret master) {
+ return send("ChangeWithMasterPassword", "o(oayays)(oayays)", collection, original, master)
+ // TODO: check which condition should apply
+ // .map(Static.Utils::isNullOrEmpty)
+ .map(response -> !Static.Utils.isNullOrEmpty(response))
+ .orElse(false);
}
@Override
- public ObjectPath changeWithPrompt(DBusPath collection) {
- Object[] response = send("ChangeWithPrompt", "o", collection);
- return (ObjectPath) response[0];
+ public Optional changeWithPrompt(DBusPath collection) {
+ return send("ChangeWithPrompt", "o", collection)
+ .flatMap(response -> Static.Convert.toObjectPath(response[0]));
}
@Override
- public ObjectPath createWithMasterPassword(Map properties, Secret master) {
- Object[] response = send("CreateWithMasterPassword", "a{sv}(oayays)", properties, master);
- return (ObjectPath) response[0];
+ public Optional createWithMasterPassword(Map properties, Secret master) {
+ return send("CreateWithMasterPassword", "a{sv}(oayays)", properties, master)
+ .flatMap(response -> Static.Convert.toObjectPath(response[0]));
}
@Override
- public void unlockWithMasterPassword(DBusPath collection, Secret master) {
- send("UnlockWithMasterPassword", "o(oayays)", collection, master);
+ public boolean unlockWithMasterPassword(DBusPath collection, Secret master) {
+ return send("UnlockWithMasterPassword", "o(oayays)", collection, master)
+ // TODO: check which condition should apply
+ // .map(Static.Utils::isNullOrEmpty)
+ .map(response -> !Static.Utils.isNullOrEmpty(response))
+ .orElse(false);
}
@Override
diff --git a/src/main/java/de/swiesend/secretservice/gnome/keyring/interfaces/InternalUnsupportedGuiltRiddenInterface.java b/src/main/java/de/swiesend/secretservice/gnome/keyring/interfaces/InternalUnsupportedGuiltRiddenInterface.java
index 6a7b5f1..f281508 100644
--- a/src/main/java/de/swiesend/secretservice/gnome/keyring/interfaces/InternalUnsupportedGuiltRiddenInterface.java
+++ b/src/main/java/de/swiesend/secretservice/gnome/keyring/interfaces/InternalUnsupportedGuiltRiddenInterface.java
@@ -8,6 +8,7 @@
import de.swiesend.secretservice.Secret;
import java.util.Map;
+import java.util.Optional;
@DBusInterfaceName("org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface")
public interface InternalUnsupportedGuiltRiddenInterface extends DBusInterface {
@@ -21,7 +22,7 @@ public interface InternalUnsupportedGuiltRiddenInterface extends DBusInterface {
* @param original The current password.
* @param master The new password.
*/
- void changeWithMasterPassword(DBusPath collection, Secret original, Secret master);
+ boolean changeWithMasterPassword(DBusPath collection, Secret original, Secret master);
/**
* Toggle the lock of a collection.
@@ -30,7 +31,7 @@ public interface InternalUnsupportedGuiltRiddenInterface extends DBusInterface {
*
* @return The ObjectPath of the collection.
*/
- ObjectPath changeWithPrompt(DBusPath collection);
+ Optional changeWithPrompt(DBusPath collection);
/**
* Create a collection with a password without prompting.
@@ -40,7 +41,7 @@ public interface InternalUnsupportedGuiltRiddenInterface extends DBusInterface {
*
* @return The ObjectPath of the created collection.
*/
- ObjectPath createWithMasterPassword(Map properties, Secret master);
+ Optional createWithMasterPassword(Map properties, Secret master);
/**
* Unlock a collection without prompting.
@@ -48,6 +49,6 @@ public interface InternalUnsupportedGuiltRiddenInterface extends DBusInterface {
* @param collection The ObjectPath of the collection.
* @param master The password of the collection.
*/
- void unlockWithMasterPassword(DBusPath collection, Secret master);
+ boolean unlockWithMasterPassword(DBusPath collection, Secret master);
}
diff --git a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
index 17e9d37..32d2166 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
@@ -1,6 +1,5 @@
package de.swiesend.secretservice.handlers;
-import org.apache.commons.lang3.ArrayUtils;
import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.messages.MethodCall;
@@ -19,6 +18,7 @@ public class MessageHandler {
private Logger log = LoggerFactory.getLogger(getClass());
private DBusConnection connection;
+ private boolean fireAndForget = true;
public MessageHandler(DBusConnection connection) {
this.connection = connection;
@@ -48,7 +48,7 @@ public Optional send(String service, String path, String iface, String
switch (error) {
case "org.freedesktop.Secret.Error.NoSession":
case "org.freedesktop.Secret.Error.NoSuchObject":
- if (ArrayUtils.isEmpty(parameters)) {
+ if (Static.Utils.isNullOrEmpty(parameters)) {
log.warn(error);
} else {
log.warn(error + ": " + parameters[0]);
@@ -56,7 +56,7 @@ public Optional send(String service, String path, String iface, String
return Optional.empty();
case "org.gnome.keyring.Error.Denied":
case "org.freedesktop.Secret.Error.IsLocked":
- if (ArrayUtils.isEmpty(parameters)) {
+ if (Static.Utils.isNullOrEmpty(parameters)) {
log.info(error);
} else {
log.info(error + ": " + parameters[0]);
@@ -75,7 +75,6 @@ public Optional send(String service, String path, String iface, String
return Optional.empty();
}
}
-
return Optional.ofNullable(parameters);
} catch (DBusException e) {
log.error("Unexpected D-Bus response: ", e);
@@ -90,7 +89,7 @@ public Optional getProperty(String service, String path, String iface,
"Get", "ss", iface, property);
if (!maybeResponse.isPresent()) return Optional.empty();
Object[] response = maybeResponse.get();
- return ArrayUtils.isEmpty(response) ? Optional.empty() : Optional.ofNullable((Variant) response[0]);
+ return Static.Utils.isNullOrEmpty(response) ? Optional.empty() : Optional.ofNullable((Variant) response[0]);
}
public Optional getAllProperties(String service, String path, String iface) {
@@ -98,11 +97,28 @@ public Optional getAllProperties(String service, String path, String if
"GetAll", "ss", iface);
if (!maybeResponse.isPresent()) return Optional.empty();
Object[] response = maybeResponse.get();
- return ArrayUtils.isEmpty(response) ? Optional.empty() : Optional.ofNullable((Variant) response[0]);
+ return Static.Utils.isNullOrEmpty(response) ? Optional.empty() : Optional.ofNullable((Variant) response[0]);
}
public boolean setProperty(String service, String path, String iface, String property, Variant value) {
Optional maybeResponse = send(service, path, Static.DBus.Interfaces.DBUS_PROPERTIES, "Set", "ssv", iface, property, value);
+ // TODO: resolve return value
+// if (maybeResponse.isPresent() && !fireAndForget) {
+// Optional maybeValue = getProperty(service, path, iface, property);
+// if (maybeValue.isPresent()) {
+// Variant result = maybeValue.get();
+// if (result == null) return false;
+// boolean valueCond = value.getValue() == result.getValue();
+// boolean signatureCond = value.getSig() == result.getSig();
+// boolean typeCond = value.getType() == result.getType();
+// return valueCond && signatureCond && typeCond;
+// } else {
+// return false;
+// }
+// } else {
+// return maybeResponse.isPresent();
+// }
+ //return true;
return maybeResponse.isPresent();
}
diff --git a/src/main/java/de/swiesend/secretservice/interfaces/Collection.java b/src/main/java/de/swiesend/secretservice/interfaces/Collection.java
index 417250e..ad0d71b 100644
--- a/src/main/java/de/swiesend/secretservice/interfaces/Collection.java
+++ b/src/main/java/de/swiesend/secretservice/interfaces/Collection.java
@@ -14,6 +14,7 @@
import java.util.List;
import java.util.Map;
+import java.util.Optional;
@DBusInterfaceName(Static.Interfaces.COLLECTION)
public interface Collection extends DBusInterface {
@@ -21,113 +22,56 @@ public interface Collection extends DBusInterface {
/**
* The key of the D-Bus properties for the label of a collection.
*/
- public static final String LABEL = "org.freedesktop.Secret.Collection.Label";
-
- public static class ItemCreated extends DBusSignal {
- public final DBusPath item;
-
- /**
- * A new item in this collection was created.
- *
- * @param path The path to the object this is emitted from.
- * @param item The item that was created.
- *
- * @throws DBusException Could not communicate properly with the D-Bus.
- */
- public ItemCreated(String path, DBusPath item) throws DBusException {
- super(path, item);
- this.item = item;
- }
- }
-
- public static class ItemDeleted extends DBusSignal {
- public final DBusPath item;
-
- /**
- * An item in this collection was deleted.
- *
- * @param path The path to the object this is emitted from.
- * @param item The item that was deleted.
- *
- * @throws DBusException Could not communicate properly with the D-Bus.
- */
- public ItemDeleted(String path, DBusPath item) throws DBusException {
- super(path, item);
- this.item = item;
- }
- }
-
- public static class ItemChanged extends DBusSignal {
- public final DBusPath item;
-
- /**
- * An item in this collection changed.
- *
- * @param path The path to the object this is emitted from.
- * @param item The item that was changed.
- *
- * @throws DBusException Could not communicate properly with the D-Bus.
- */
- public ItemChanged(String path, DBusPath item) throws DBusException {
- super(path, item);
- this.item = item;
- }
- }
+ String LABEL = "org.freedesktop.Secret.Collection.Label";
/**
* Delete this collection.
*
* @return prompt — A prompt to delete the collection, or the special value '/' when no prompt is necessary.
- *
* @see ObjectPath
*/
- abstract public ObjectPath delete();
+ Optional delete();
/**
* Search for items in this collection matching the lookup attributes.
*
- * @param attributes Attributes to match.
- *
+ * @param attributes Attributes to match.
* @return results — Items that matched the attributes.
- *
* @see ObjectPath
* @see Secret
* @see Item
*/
- abstract public List searchItems(Map attributes);
+ Optional> searchItems(Map attributes);
/**
* Create an item with the given attributes, secret and label. If replace is set, then it replaces an item already
* present with the same values for the attributes.
*
- * @param properties The properties for the new item.
- *
- * This allows setting the new item's properties upon its creation. All READWRITE properties
- * are useable. Specify the property names in full interface.Property
form.
- *
- *
- * Example 13.2. Example for properties of an item:
- *
- * properties = {
- * "org.freedesktop.Secret.Item.Label": "MyItem",
- * "org.freedesktop.Secret.Item.Attributes": {
- * "Attribute1": "Value1",
- * "Attribute2": "Value2"
- * }
- * }
- *
- *
- *
- * Note:
- * Please note that there is a distinction between the terms Property , which refers
- * to D-Bus properties of an object, and Attribute , which refers to one of a
- * secret item's string-valued attributes.
- *
- *
- * @param secret The secret to store in the item, encoded with the included session.
- *
- * @param replace Whether to replace an item with the same attributes or not.
- *
+ * @param properties The properties for the new item.
+ *
+ * This allows setting the new item's properties upon its creation. All READWRITE properties
+ * are usable. Specify the property names in full interface.Property
form.
+ *
+ *
+ * Example 13.2. Example for properties of an item:
+ *
+ * properties = {
+ * "org.freedesktop.Secret.Item.Label": "MyItem",
+ * "org.freedesktop.Secret.Item.Attributes": {
+ * "Attribute1": "Value1",
+ * "Attribute2": "Value2"
+ * }
+ * }
+ *
+ *
+ *
+ * Note:
+ * Please note that there is a distinction between the terms Property , which refers
+ * to D-Bus properties of an object, and Attribute , which refers to one of a
+ * secret item's string-valued attributes.
+ *
+ * @param secret The secret to store in the item, encoded with the included session.
+ * @param replace Whether to replace an item with the same attributes or not.
* @return Pair<item, prompt>
*
* item — The item created, or the special value '/' if a prompt is necessary.
@@ -143,58 +87,107 @@ public ItemChanged(String path, DBusPath item) throws DBusException {
* @see Secret
* @see Item
*/
- abstract public Pair createItem(Map properties, Secret secret, boolean replace);
+ Optional> createItem(Map properties, Secret secret, boolean replace);
/**
* Items is a D-Bus Property.
*
* It is managed by using the org.freedesktop.DBus.Properties
interface.
*
- * @return Items in this collection.
+ * @return Items in this collection.
*/
- abstract public List getItems();
+ Optional> getItems();
/**
* Label is a D-Bus Property.
*
* It is managed by using the org.freedesktop.DBus.Properties
interface.
*
- * @return The displayable label of this collection.
+ * @return The displayable label of this collection.
*
- *
- * Note:
- * The displayable label
can differ from the actual name
of a collection.
- *
+ *
+ * Note:
+ * The displayable label
can differ from the actual name
of a collection.
+ *
*/
- abstract public String getLabel();
+ Optional getLabel();
/**
* Label is a D-Bus Property.
*
* It is managed by using the org.freedesktop.DBus.Properties
interface.
*
- * @param label The displayable label of this collection.
+ * @param label The displayable label of this collection.
*
- *
- * Note:
- * The displayable label
can differ from the actual name
of a collection.
- *
+ *
+ * Note:
+ * The displayable label
can differ from the actual name
of a collection.
+ *
+ * @return
*/
- abstract public void setLabel(String label);
+ boolean setLabel(String label);
/**
- * @return Whether the collection is locked and must be authenticated by the client application.
+ * @return Whether the collection is locked and must be authenticated by the client application.
*/
- abstract public boolean isLocked();
+ boolean isLocked();
/**
- * @return The unix time when the collection was created.
+ * @return The unix time when the collection was created.
*/
- abstract public UInt64 created();
+ Optional created();
/**
- * @return The unix time when the collection was last modified.
+ * @return The unix time when the collection was last modified.
*/
- abstract public UInt64 modified();
+ Optional modified();
+
+ class ItemCreated extends DBusSignal {
+ public final DBusPath item;
+
+ /**
+ * A new item in this collection was created.
+ *
+ * @param path The path to the object this is emitted from.
+ * @param item The item that was created.
+ * @throws DBusException Could not communicate properly with the D-Bus.
+ */
+ public ItemCreated(String path, DBusPath item) throws DBusException {
+ super(path, item);
+ this.item = item;
+ }
+ }
+
+ class ItemDeleted extends DBusSignal {
+ public final DBusPath item;
+
+ /**
+ * An item in this collection was deleted.
+ *
+ * @param path The path to the object this is emitted from.
+ * @param item The item that was deleted.
+ * @throws DBusException Could not communicate properly with the D-Bus.
+ */
+ public ItemDeleted(String path, DBusPath item) throws DBusException {
+ super(path, item);
+ this.item = item;
+ }
+ }
+
+ class ItemChanged extends DBusSignal {
+ public final DBusPath item;
+
+ /**
+ * An item in this collection changed.
+ *
+ * @param path The path to the object this is emitted from.
+ * @param item The item that was changed.
+ * @throws DBusException Could not communicate properly with the D-Bus.
+ */
+ public ItemChanged(String path, DBusPath item) throws DBusException {
+ super(path, item);
+ this.item = item;
+ }
+ }
}
diff --git a/src/main/java/de/swiesend/secretservice/interfaces/Item.java b/src/main/java/de/swiesend/secretservice/interfaces/Item.java
index 8c2ec99..3e26382 100644
--- a/src/main/java/de/swiesend/secretservice/interfaces/Item.java
+++ b/src/main/java/de/swiesend/secretservice/interfaces/Item.java
@@ -8,6 +8,7 @@
import de.swiesend.secretservice.Static;
import java.util.Map;
+import java.util.Optional;
@DBusInterfaceName(Static.Interfaces.ITEM)
public interface Item extends DBusInterface {
@@ -15,19 +16,19 @@ public interface Item extends DBusInterface {
/**
* The key of of the D-Bus properties for the label of an item.
*/
- public static final String LABEL = "org.freedesktop.Secret.Item.Label";
+ String LABEL = "org.freedesktop.Secret.Item.Label";
/**
* The key of the D-Bus properties for the attributes of an item.
*/
- public static final String ATTRIBUTES = "org.freedesktop.Secret.Item.Attributes";
+ String ATTRIBUTES = "org.freedesktop.Secret.Item.Attributes";
/**
* Delete this item.
*
* @return Prompt — A prompt objectpath, or the special value '/' if no prompt is necessary.
*/
- abstract public ObjectPath delete();
+ Optional delete();
/**
* Retrieve the secret for this item.
@@ -35,19 +36,20 @@ public interface Item extends DBusInterface {
* @param session The session to use to encode the secret.
* @return secret — The secret retrieved.
*/
- abstract public Secret getSecret(ObjectPath session);
+ Optional getSecret(ObjectPath session);
/**
* Set the secret for this item.
*
* @param secret The secret to set, encoded for the included session.
+ * @return
*/
- abstract public void setSecret(Secret secret);
+ boolean setSecret(Secret secret);
/**
* @return Whether the item is locked and requires authentication, or not.
*/
- abstract public boolean isLocked();
+ boolean isLocked();
/**
* The lookup attributes for this item.
@@ -58,7 +60,7 @@ public interface Item extends DBusInterface {
*
* @return The attributes of the item.
*/
- abstract public Map getAttributes();
+ Optional> getAttributes();
/**
@@ -69,8 +71,9 @@ public interface Item extends DBusInterface {
* It is managed by using the org.freedesktop.DBus.Properties
interface.
*
* @param attributes The attributes of the item.
+ * @return
*/
- abstract public void setAttributes(Map attributes);
+ boolean setAttributes(Map attributes);
/**
* Label is a D-Bus Property.
@@ -84,7 +87,7 @@ public interface Item extends DBusInterface {
* The displayable label
can differ from the actual name
of a collection.
*
*/
- abstract public String getLabel();
+ Optional getLabel();
/**
@@ -98,22 +101,23 @@ public interface Item extends DBusInterface {
* Note:
* The displayable label
can differ from the actual name
of a collection.
*
+ * @return
*/
- abstract public void setLabel(String label);
+ boolean setLabel(String label);
/**
* @return The "xdg:schema" of the item attributes.
*/
- abstract public String getType();
+ Optional getType();
/**
* @return The unix time when the item was created.
*/
- abstract public UInt64 created();
+ Optional created();
/**
* @return The unix time when the item was last modified.
*/
- abstract public UInt64 modified();
+ Optional modified();
}
diff --git a/src/main/java/de/swiesend/secretservice/interfaces/Prompt.java b/src/main/java/de/swiesend/secretservice/interfaces/Prompt.java
index 8ff525a..c128dbf 100644
--- a/src/main/java/de/swiesend/secretservice/interfaces/Prompt.java
+++ b/src/main/java/de/swiesend/secretservice/interfaces/Prompt.java
@@ -12,7 +12,7 @@
@DBusInterfaceName(Static.Interfaces.PROMPT)
public interface Prompt extends DBusInterface {
- public static class Completed extends DBusSignal {
+ class Completed extends DBusSignal {
public final boolean dismissed;
public final Variant result;
@@ -40,21 +40,20 @@ public Completed(String path, boolean dismissed, Variant result) throws DBusExce
*
* @see Completed
*/
- abstract public void prompt(String window_id);
+ boolean prompt(String window_id);
/**
* Perform the prompt.
*
* @param prompt Objectpath of the prompt.
- * @throws NoSuchObject No such item or collection exists.
*
* @see Completed
*/
- abstract public void prompt(ObjectPath prompt) throws NoSuchObject;
+ boolean prompt(ObjectPath prompt);
/**
* Dismiss the prompt.
*/
- abstract public void dismiss();
+ boolean dismiss();
}
diff --git a/src/main/java/de/swiesend/secretservice/interfaces/Service.java b/src/main/java/de/swiesend/secretservice/interfaces/Service.java
index 9a9557a..200de95 100644
--- a/src/main/java/de/swiesend/secretservice/interfaces/Service.java
+++ b/src/main/java/de/swiesend/secretservice/interfaces/Service.java
@@ -14,11 +14,12 @@
import java.util.List;
import java.util.Map;
+import java.util.Optional;
@DBusInterfaceName(Static.Interfaces.SERVICE)
public interface Service extends DBusInterface {
- public static class CollectionCreated extends DBusSignal {
+ class CollectionCreated extends DBusSignal {
public final DBusPath collection;
/**
@@ -36,7 +37,7 @@ public CollectionCreated(String path, DBusPath collection) throws DBusException
}
}
- public static class CollectionDeleted extends DBusSignal {
+ class CollectionDeleted extends DBusSignal {
public final DBusPath collection;
/**
@@ -54,7 +55,7 @@ public CollectionDeleted(String path, DBusPath collection) throws DBusException
}
}
- public static class CollectionChanged extends DBusSignal {
+ class CollectionChanged extends DBusSignal {
public final DBusPath collection;
/**
@@ -77,7 +78,7 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
*
* @param algorithm The algorithm the caller wishes to use.
* @param input Input arguments for the algorithm.
- *
+ *
* @return Pair<output, result>
*
* output — Output of the session algorithm negotiation.
@@ -88,7 +89,7 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* @see Variant
* @see ObjectPath
*/
- abstract public Pair, ObjectPath> openSession(String algorithm, Variant input);
+ Optional, ObjectPath>> openSession(String algorithm, Variant input);
/**
* Create a new collection with the specified properties.
@@ -101,15 +102,15 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
*
* properties = { "org.freedesktop.Secret.Collection.Label": "MyCollection" }
*
- *
- * @param alias If creating this connection for a well known alias then a string like "default"
.
- * If an collection with this well-known alias already exists, then that collection will be
- * returned instead of creating a new collection. Any readwrite properties provided to this
+ *
+ * @param alias If creating this connection for a well known alias then a string like "default"
.
+ * If an collection with this well-known alias already exists, then that collection will be
+ * returned instead of creating a new collection. Any readwrite properties provided to this
* function will be set on the collection.
*
- * Set this to an empty string if the new collection should not be associated with a well
+ * Set this to an empty string if the new collection should not be associated with a well
* known alias.
- *
+ *
* @return Pair<collection, prompt>
*
* collection — The new collection object, or '/' if prompting is necessary.
@@ -120,7 +121,7 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* @see Variant
* @see ObjectPath
*/
- abstract public Pair createCollection(Map properties, String alias);
+ Optional> createCollection(Map properties, String alias);
/**
* Create a new collection with the specified properties.
@@ -133,7 +134,7 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
*
* properties = { "org.freedesktop.Secret.Collection.Label": "MyCollection" }
*
- *
+ *
* @return Pair<collection, prompt>
*
* collection — The new collection object, or '/' if prompting is necessary.
@@ -144,13 +145,13 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* @see Variant
* @see ObjectPath
*/
- abstract public Pair createCollection(Map properties);
+ Optional> createCollection(Map properties);
/**
* Find items in any collection.
*
* @param attributes Find secrets in any collection.
- *
+ *
*
* Example:
* {
@@ -158,14 +159,14 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* "Attribute2": "Value2"
* }
*
- *
+ *
*
* Note:
- * Please note that there is a distinction between the terms Property , which refers
- * to D-Bus properties of an object, and Attribute , which refers to one of a
+ * Please note that there is a distinction between the terms Property , which refers
+ * to D-Bus properties of an object, and Attribute , which refers to one of a
* secret item's string-valued attributes.
*
- *
+ *
* @return Pair<unlocked, locked>
*
* unlocked — Items found.
@@ -175,13 +176,13 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* @see Pair
* @see ObjectPath
*/
- abstract public Pair, List> searchItems(Map attributes);
+ Optional, List>> searchItems(Map attributes);
/**
* Unlock the specified objects.
*
* @param objects Objects to unlock.
- *
+ *
* @return Pair<unlocked, prompt>
*
* unlocked — Objects that were unlocked without a prompt.
@@ -191,13 +192,13 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* @see Pair
* @see ObjectPath
*/
- abstract public Pair, ObjectPath> unlock(List objects);
+ Optional, ObjectPath>> unlock(List objects);
/**
* Lock the items.
*
* @param objects Objects to lock.
- *
+ *
* @return Pair<locked, prompt>
*
* locked — Objects that were locked without a prompt.
@@ -206,7 +207,7 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
*
* @see Pair
*/
- abstract public Pair, ObjectPath> lock(List objects);
+ Optional, ObjectPath>> lock(List objects);
/**
* Lock the entire Secret Service API.
@@ -217,7 +218,7 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* {@link #unlock(List objects)}
* {@link InternalUnsupportedGuiltRiddenInterface#unlockWithMasterPassword(DBusPath collection, Secret master)}
*/
- abstract public void lockService();
+ boolean lockService();
/**
* Toggle the lock for a collection with a prompt.
@@ -230,34 +231,34 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* See Also:
* {@link InternalUnsupportedGuiltRiddenInterface#changeWithPrompt(DBusPath collection)}
*/
- abstract public ObjectPath changeLock(ObjectPath collection);
+ Optional changeLock(ObjectPath collection);
/**
* Retrieve multiple secrets from different items.
*
* @param items Items to get secrets for.
- *
+ *
* @param session The session to use to encode the secrets.
- *
+ *
* @return secrets — Secrets for the items.
*
* @see Secret
* @see ObjectPath
*/
- abstract public Map getSecrets(List items, ObjectPath session);
+ Optional> getSecrets(List items, ObjectPath session);
/**
* Get the collection with the given alias.
*
* @param name An alias, such as 'default'.
- *
+ *
* @return collection — The collection or the the path '/' if no such collection exists.
*
* @see Static.ObjectPaths
* @see ObjectPath
* @see Collection
*/
- abstract public ObjectPath readAlias(String name);
+ Optional readAlias(String name);
/**
* Setup a collection alias.
@@ -269,11 +270,11 @@ public CollectionChanged(String path, DBusPath collection) throws DBusException
* @see ObjectPath
* @see Collection
*/
- abstract public void setAlias(String name, ObjectPath collection);
+ boolean setAlias(String name, ObjectPath collection);
/**
* @return A list of present collections.
*/
- abstract public List getCollections();
+ Optional> getCollections();
}
diff --git a/src/main/java/de/swiesend/secretservice/interfaces/Session.java b/src/main/java/de/swiesend/secretservice/interfaces/Session.java
index 64ceacc..b131926 100644
--- a/src/main/java/de/swiesend/secretservice/interfaces/Session.java
+++ b/src/main/java/de/swiesend/secretservice/interfaces/Session.java
@@ -9,7 +9,9 @@ public interface Session extends DBusInterface {
/**
* Close this session.
+ *
+ * @return true if closed else false
*/
- abstract public void close();
+ boolean close();
}
diff --git a/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java b/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
index 140a9fa..0c76b50 100644
--- a/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
+++ b/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
@@ -32,12 +32,13 @@
import static de.swiesend.secretservice.Static.DBus.DEFAULT_DELAY_MILLIS;
import static de.swiesend.secretservice.Static.DBus.MAX_DELAY_MILLIS;
import static de.swiesend.secretservice.Static.DEFAULT_PROMPT_TIMEOUT;
+import static de.swiesend.secretservice.Static.Utils;
public final class SimpleCollection extends de.swiesend.secretservice.simple.interfaces.SimpleCollection {
private static final Logger log = LoggerFactory.getLogger(SimpleCollection.class);
- private static Thread shutdownHook = setupShutdownHook();
- private static DBusConnection connection = getConnection();
+ private static final DBusConnection connection = getConnection();
+ private static final Thread shutdownHook = setupShutdownHook();
private TransportEncryption transport = null;
private Service service = null;
private Session session = null;
@@ -102,13 +103,13 @@ public SimpleCollection(String label, CharSequence password) throws IOException
Map properties = Collection.createProperties(label);
if (password == null) {
- Pair response = service.createCollection(properties);
+ Pair response = service.createCollection(properties).get();
if (!"/".equals(response.a.getPath())) {
path = response.a;
}
performPrompt(response.b);
- } else if (encrypted != null && withoutPrompt != null) {
- path = withoutPrompt.createWithMasterPassword(properties, encrypted);
+ } else if (encrypted != null) {
+ path = withoutPrompt.createWithMasterPassword(properties, encrypted).get();
}
if (path == null) {
@@ -179,7 +180,8 @@ public static boolean isAvailable() {
List names = Arrays.asList(bus.ListNames());
if (!(names.containsAll(Arrays.asList(
Static.DBus.Service.DBUS,
- Static.Service.SECRETS)))) {
+ Static.Service.SECRETS,
+ de.swiesend.secretservice.gnome.keyring.Static.Service.KEYRING)))) {
return false;
}
@@ -297,12 +299,12 @@ private void init() throws IOException {
}
private Map getLabels() {
- List collections = service.getCollections();
+ List collections = service.getCollections().get();
Map labels = new HashMap();
for (ObjectPath path : collections) {
Collection c = new Collection(path, service, null);
- labels.put(path, c.getLabel());
+ labels.put(path, c.getLabel().get());
}
return labels;
@@ -375,7 +377,7 @@ private void unlock() {
withoutPrompt.unlockWithMasterPassword(collection.getPath(), encrypted);
log.debug("Unlocked collection: " + collection.getLabel() + " (" + collection.getObjectPath() + ")");
} else {
- Pair, ObjectPath> response = service.unlock(lockable());
+ Pair, ObjectPath> response = service.unlock(lockable()).get();
performPrompt(response.b);
if (!collection.isLocked()) {
isUnlockedOnceWithUserPermission = true;
@@ -441,7 +443,7 @@ public void close() {
@Override
public void delete() throws AccessControlException {
if (!isDefault()) {
- ObjectPath promptPath = collection.delete();
+ ObjectPath promptPath = collection.delete().get();
performPrompt(promptPath);
} else {
throw new AccessControlException("Default collections may not be deleted with the simple API.");
@@ -460,7 +462,7 @@ public void delete() throws AccessControlException {
@Override
public String createItem(String label, CharSequence password, Map attributes) throws IllegalArgumentException {
- if (Static.isNullOrEmpty(password)) {
+ if (Utils.isNullOrEmpty(password)) {
throw new IllegalArgumentException("The password may not be null or empty.");
}
if (label == null) {
@@ -474,7 +476,7 @@ public String createItem(String label, CharSequence password, Map properties = Item.createProperties(label, attributes);
try (final Secret secret = transport.encrypt(password)) {
- Pair response = collection.createItem(properties, secret, false);
+ Pair response = collection.createItem(properties, secret, false).get();
if (response == null) return null;
item = response.a;
if ("/".equals(item.getPath())) {
@@ -525,7 +527,7 @@ public String createItem(String label, CharSequence password) throws IllegalArgu
@Override
public void updateItem(String objectPath, String label, CharSequence password, Map attributes) throws IllegalArgumentException {
- if (Static.isNullOrEmpty(objectPath)) {
+ if (Utils.isNullOrEmpty(objectPath)) {
throw new IllegalArgumentException("The object path of the item may not be null or empty.");
}
@@ -561,9 +563,9 @@ public void updateItem(String objectPath, String label, CharSequence password, M
*/
@Override
public String getLabel(String objectPath) {
- if (Static.isNullOrEmpty(objectPath)) return null;
+ if (Utils.isNullOrEmpty(objectPath)) return null;
unlock();
- return getItem(objectPath).getLabel();
+ return getItem(objectPath).getLabel().get();
}
/**
@@ -576,9 +578,9 @@ public String getLabel(String objectPath) {
*/
@Override
public Map getAttributes(String objectPath) {
- if (Static.isNullOrEmpty(objectPath)) return null;
+ if (Utils.isNullOrEmpty(objectPath)) return null;
unlock();
- return getItem(objectPath).getAttributes();
+ return getItem(objectPath).getAttributes().get();
}
/**
@@ -592,7 +594,7 @@ public List getItems(Map attributes) {
if (attributes == null) return null;
unlock();
- List objects = collection.searchItems(attributes);
+ List objects = collection.searchItems(attributes).get();
if (objects != null && !objects.isEmpty()) {
return Static.Convert.toStrings(objects);
@@ -609,13 +611,15 @@ public List getItems(Map attributes) {
*/
@Override
public char[] getSecret(String objectPath) {
- if (Static.isNullOrEmpty(objectPath)) return null;
+ if (Utils.isNullOrEmpty(objectPath)) return null;
unlock();
final Item item = getItem(objectPath);
char[] decrypted = null;
- try (final Secret secret = item.getSecret(session.getPath())) {
+ ObjectPath sessionPath = session.getPath();
+ log.info(sessionPath.getPath());
+ try (final Secret secret = item.getSecret(sessionPath).orElseGet(() -> new Secret(sessionPath, null))) {
decrypted = transport.decrypt(secret);
} catch (NoSuchPaddingException |
NoSuchAlgorithmException |
@@ -641,7 +645,7 @@ public char[] getSecret(String objectPath) {
public Map getSecrets() throws AccessControlException {
unlockWithUserPermission();
- List items = collection.getItems();
+ List items = collection.getItems().get();
if (items == null) return null;
Map passwords = new HashMap();
@@ -664,12 +668,12 @@ public Map getSecrets() throws AccessControlException {
*/
@Override
public void deleteItem(String objectPath) throws AccessControlException {
- if (Static.isNullOrEmpty(objectPath)) throw new AccessControlException("Cannot delete an unspecified item.");
+ if (Utils.isNullOrEmpty(objectPath)) throw new AccessControlException("Cannot delete an unspecified item.");
unlockWithUserPermission();
Item item = getItem(objectPath);
- ObjectPath promptPath = item.delete();
+ ObjectPath promptPath = item.delete().get();
performPrompt(promptPath);
}
diff --git a/src/test/java/de/swiesend/secretservice/integration/CollectionTest.java b/src/test/java/de/swiesend/secretservice/integration/CollectionTest.java
index c73ebe5..7557fec 100644
--- a/src/test/java/de/swiesend/secretservice/integration/CollectionTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/CollectionTest.java
@@ -41,13 +41,13 @@ public void afterEach() {
@Test
@DisplayName("delete test collection")
public void delete() {
- List expected = context.service.getCollections();
- ObjectPath promptPath = context.collection.delete();
+ List expected = context.service.getCollections().get();
+ ObjectPath promptPath = context.collection.delete().get();
log.info(promptPath.toString());
assertEquals("/", promptPath.getPath());
// assertTrue(promptPath.getPath().startsWith("/org/freedesktop/secrets/prompt/p"));
- List actual = context.service.getCollections();
+ List actual = context.service.getCollections().get();
assertEquals(expected.size() - 1, actual.size());
}
@@ -57,7 +57,7 @@ public void searchItems() {
Map attributes = new HashMap();
attributes.put("Attribute1", "Value1");
- List items = context.collection.searchItems(attributes);
+ List items = context.collection.searchItems(attributes).get();
log.info(Arrays.toString(items.toArray()));
assertEquals(1, items.size());
assertTrue(items.get(0).getPath().startsWith("/org/freedesktop/secrets/collection/test/"));
@@ -75,22 +75,22 @@ public void createItem() {
attributes.put("Attribute1", "Value1");
Map properties = Item.createProperties("TestItem", attributes);
- Pair response = context.collection.createItem(properties, secret, true);
+ Pair response = context.collection.createItem(properties, secret, true).get();
log.info(response.toString());
assertTrue(response.a.getPath().startsWith("/org/freedesktop/secrets/collection/test/"));
assertEquals("/", response.b.getPath());
- List items = context.collection.getItems();
+ List items = context.collection.getItems().get();
assertEquals(1, items.size());
context.collection.createItem(properties, secret, false);
- items = context.collection.getItems();
+ items = context.collection.getItems().get();
assertEquals(2, items.size());
}
@Test
public void getItems() {
- List items = context.collection.getItems();
+ List items = context.collection.getItems().get();
log.info(Arrays.toString(items.toArray()));
assertEquals(1, items.size());
assertTrue(items.get(0).getPath().startsWith("/org/freedesktop/secrets/collection/test/"));
@@ -100,7 +100,7 @@ public void getItems() {
@Disabled
public void getLabel() {
Collection collection = new Collection("login", context.service);
- String response = collection.getLabel();
+ String response = collection.getLabel().get();
log.info(response);
List labels = Arrays.asList(new String[]{
"login", // en
@@ -114,15 +114,15 @@ public void getLabel() {
@Test
public void setLabel() {
- String label = context.collection.getLabel();
+ String label = context.collection.getLabel().get();
assertEquals("test", label);
context.collection.setLabel("test-renamed");
- label = context.collection.getLabel();
+ label = context.collection.getLabel().get();
assertEquals("test-renamed", label);
context.collection.setLabel("test");
- label = context.collection.getLabel();
+ label = context.collection.getLabel().get();
assertEquals("test", label);
}
@@ -140,17 +140,17 @@ public void created() {
UInt64 response;
collection = new Collection("test", context.service);
- response = collection.created();
+ response = collection.created().get();
log.info("test: " + response);
assertTrue(response.longValue() >= 0L);
collection = new Collection("login", context.service);
- response = collection.created();
+ response = collection.created().get();
log.info("login: " + response);
assertTrue(response.longValue() >= 0L);
collection = new Collection("session", context.service);
- response = collection.created();
+ response = collection.created().get();
log.info("session: " + response);
assertTrue(response.longValue() == 0L);
}
@@ -162,16 +162,16 @@ public void modified() {
UInt64 response;
collection = new Collection("test", context.service);
- response = collection.modified();
+ response = collection.modified().get();
log.info("test: " + response);
assertTrue(response.longValue() >= 0L);
collection = new Collection("login", context.service);
- response = collection.modified();
+ response = collection.modified().get();
log.info("login: " + response);
assertTrue(response.longValue() >= 0L);
collection = new Collection("session", context.service);
- response = collection.modified();
+ response = collection.modified().get();
log.info("session: " + response);
assertTrue(response.longValue() == 0L);
}
diff --git a/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java b/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
index ce0c5e9..3890f46 100644
--- a/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
@@ -60,14 +60,14 @@ public void testWithTransportEncryption() throws
InternalUnsupportedGuiltRiddenInterface noPrompt = new InternalUnsupportedGuiltRiddenInterface(service);
Secret master = transportEncryption.encrypt("test");
Collection collection = new Collection("test", service);
- List collections = Static.Convert.toStrings(service.getCollections());
+ List collections = Static.Convert.toStrings(service.getCollections().get());
if (collections.contains(collection.getObjectPath())) {
noPrompt.unlockWithMasterPassword(collection.getPath(), master);
} else {
HashMap properties = new HashMap();
properties.put("org.freedesktop.Secret.Collection.Label", new Variant("test"));
- ObjectPath collectionPath = noPrompt.createWithMasterPassword(properties, master);
+ ObjectPath collectionPath = noPrompt.createWithMasterPassword(properties, master).get();
log.info("created collection: " + collectionPath.getPath());
}
@@ -86,13 +86,13 @@ public void testWithTransportEncryption() throws
noPrompt.unlockWithMasterPassword(collection.getPath(), master);
}
- Pair createItemResponse = collection.createItem(properties, encrypted, true);
+ Pair createItemResponse = collection.createItem(properties, encrypted, true).get();
log.info("await signal: Collection.ItemCreated");
Thread.currentThread().sleep(50L);
ObjectPath itemPath = createItemResponse.a;
Item item = new Item(itemPath, service);
- Secret actual = item.getSecret(session.getPath());
+ Secret actual = item.getSecret(session.getPath()).get();
assertEquals(encrypted.getSession(), actual.getSession());
assertEquals(encrypted.getContentType(), actual.getContentType());
diff --git a/src/test/java/de/swiesend/secretservice/integration/ItemTest.java b/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
index 771abc1..9a6924e 100644
--- a/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
@@ -37,20 +37,20 @@ public void afterEach() {
@Test
@DisplayName("delete item")
public void delete() {
- List items = context.collection.getItems();
+ List items = context.collection.getItems().get();
assertEquals(1, items.size());
- ObjectPath prompt = context.item.delete();
+ ObjectPath prompt = context.item.delete().get();
// expect: no prompt
assertEquals("/", prompt.getPath());
- items = context.collection.getItems();
+ items = context.collection.getItems().get();
assertEquals(0, items.size());
}
@Test
public void getSecret() {
- Secret secret = context.item.getSecret(context.session.getPath());
+ Secret secret = context.item.getSecret(context.session.getPath()).get();
log.info(label("secret", secret.toString()));
assertTrue(secret.getSession().getPath().startsWith("/org/freedesktop/secrets/session/s"));
assertTrue(secret.getContentType().startsWith("text/plain"));
@@ -76,9 +76,9 @@ public void getForeignSecret() {
DBusPath alias = new DBusPath(Static.ObjectPaths.DEFAULT_COLLECTION);
Collection login = new Collection(alias, context.service);
- List items = login.getItems();
+ List items = login.getItems().get();
Item item = new Item(items.get(0), context.service);
- Secret secret = item.getSecret(context.service.getSession().getPath());
+ Secret secret = item.getSecret(context.service.getSession().getPath()).get();
log.info(new String(secret.getSecretValue()));
}
@@ -87,7 +87,7 @@ public void setSecret() {
Secret secret = new Secret(context.session.getPath(), "new secret".getBytes());
context.item.setSecret(secret);
- Secret result = context.item.getSecret(context.session.getPath());
+ Secret result = context.item.getSecret(context.session.getPath()).get();
log.info(label("secret", result.toString()));
assertEquals("new secret", Static.Convert.toString(result.getSecretValue()));
}
@@ -101,7 +101,7 @@ public void isLocked() {
@Test
public void getAttributes() {
- Map attributes = context.item.getAttributes();
+ Map attributes = context.item.getAttributes().get();
log.info(attributes.toString());
assertTrue(attributes.size() > 0);
assertEquals("Value1", attributes.get("Attribute1"));
@@ -116,7 +116,7 @@ public void getAttributes() {
@Test
public void setAttributes() {
- Map attributes = context.item.getAttributes();
+ Map attributes = context.item.getAttributes().get();
log.info(context.item.getId());
log.info(attributes.toString());
assertTrue(attributes.size() == 3 || attributes.size() == 4);
@@ -133,7 +133,7 @@ public void setAttributes() {
context.item.setAttributes(attributes);
- attributes = context.item.getAttributes();
+ attributes = context.item.getAttributes().get();
log.info(context.item.getId());
log.info(attributes.toString());
@@ -148,7 +148,7 @@ public void setAttributes() {
attributes = new HashMap();
attributes.put("Attribute1", "Value1");
- Pair, List> result = context.service.searchItems(attributes);
+ Pair, List> result = context.service.searchItems(attributes).get();
log.info(result.toString());
assertEquals(1, result.a.size());
}
@@ -158,7 +158,7 @@ public void setAttributes() {
*/
@Test
public void getLabel() {
- String label = context.item.getLabel();
+ String label = context.item.getLabel().get();
log.info(label("label", label));
assertEquals("TestItem", label);
}
@@ -166,14 +166,14 @@ public void getLabel() {
@Test
public void setLabel() {
context.item.setLabel("RelabeledItem");
- String label = context.item.getLabel();
+ String label = context.item.getLabel().get();
log.info(label("label", label));
assertEquals("RelabeledItem", label);
}
@Test
public void getType() {
- String type = context.item.getType();
+ String type = context.item.getType().get();
log.info(type);
if (!type.isEmpty()) {
assertEquals("org.freedesktop.Secret.Generic", type);
@@ -183,7 +183,7 @@ public void getType() {
@Test
@DisplayName("created at unixtime")
public void created() {
- UInt64 created = context.item.created();
+ UInt64 created = context.item.created().get();
log.info(String.valueOf(created));
assertTrue(created.longValue() > 0L);
}
@@ -191,7 +191,7 @@ public void created() {
@Test
@DisplayName("modified at unixtime")
public void modified() {
- UInt64 modified = context.item.created();
+ UInt64 modified = context.item.created().get();
log.info(String.valueOf(modified));
assertTrue(modified.longValue() >= 0L);
}
diff --git a/src/test/java/de/swiesend/secretservice/integration/PromptTest.java b/src/test/java/de/swiesend/secretservice/integration/PromptTest.java
index 09ba406..4596550 100644
--- a/src/test/java/de/swiesend/secretservice/integration/PromptTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/PromptTest.java
@@ -46,11 +46,11 @@ public void prompt() {
cs.add(defaultCollection);
log.info("lock default collection");
- Pair, ObjectPath> locked = context.service.lock(cs);
+ Pair, ObjectPath> locked = context.service.lock(cs).get();
log.info(locked.toString());
log.info("unlock default collection");
- Pair, ObjectPath> unlocked = context.service.unlock(cs);
+ Pair, ObjectPath> unlocked = context.service.unlock(cs).get();
log.info(unlocked.toString());
ObjectPath prompt = unlocked.b;
@@ -68,7 +68,7 @@ public void dismissPrompt() throws InterruptedException {
boolean expected = defaultCollection.isLocked();
Thread.currentThread().sleep(500L);
- Pair, ObjectPath> response = context.service.unlock(cs);
+ Pair, ObjectPath> response = context.service.unlock(cs).get();
ObjectPath prompt = response.b;
assertDoesNotThrow(() ->context.prompt.prompt(prompt)); // Should not throw NoSuchObject
DBusSignal signal = handler.getLastHandledSignal();
diff --git a/src/test/java/de/swiesend/secretservice/integration/ServiceTest.java b/src/test/java/de/swiesend/secretservice/integration/ServiceTest.java
index 6cd2daf..1ed0a36 100644
--- a/src/test/java/de/swiesend/secretservice/integration/ServiceTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/ServiceTest.java
@@ -37,13 +37,13 @@ public void afterEach() {
public void openSession() {
context.ensureService();
- Pair, ObjectPath> response = context.service.openSession(Static.Algorithm.PLAIN, new Variant(""));
+ Optional, ObjectPath>> response = context.service.openSession(Static.Algorithm.PLAIN, new Variant(""));
log.info(response.toString());
- assertEquals("s", response.a.getSig());
- assertEquals("", response.a.getValue(), "the value of an empty byte[] behaves odd as it returns a String.");
+ assertEquals("s", response.get().a.getSig());
+ assertEquals("", response.get().a.getValue(), "the value of an empty byte[] behaves odd as it returns a String.");
- ObjectPath sessionPath = response.b;
+ ObjectPath sessionPath = response.get().b;
assertTrue(sessionPath.getPath().startsWith("/org/freedesktop/secrets/session/s"));
}
@@ -71,14 +71,14 @@ public void openSessionWithTransportEncryption() {
};
assertEquals(128, input.length);
- Pair, ObjectPath> response = context.service.openSession(
+ Optional, ObjectPath>> response = context.service.openSession(
Static.Algorithm.DH_IETF1024_SHA256_AES128_CBC_PKCS7, new Variant(input));
log.info(response.toString());
- byte[] peerPublicKey = response.a.getValue();
+ byte[] peerPublicKey = response.get().a.getValue();
assertEquals(128, peerPublicKey.length);
- ObjectPath sessionPath = response.b;
+ ObjectPath sessionPath = response.get().b;
assertTrue(sessionPath.getPath().startsWith(Static.ObjectPaths.SESSION + "/s"));
}
@@ -87,15 +87,15 @@ public void openSessionWithTransportEncryption() {
public void createCollection() {
context.ensureCollection();
- ObjectPath deletePrompt = context.collection.delete();
+ ObjectPath deletePrompt = context.collection.delete().orElse(new ObjectPath("", "/"));
if (!deletePrompt.getPath().equals("/")) {
context.prompt.await(deletePrompt);
}
- List before = context.service.getCollections();
+ List before = context.service.getCollections().orElse(new ArrayList());
Map properties = Collection.createProperties("test");
- Pair response = context.service.createCollection(properties);
+ Pair response = context.service.createCollection(properties).get();
log.info(response.toString());
ObjectPath collectionPath = response.a;
@@ -107,7 +107,7 @@ public void createCollection() {
assertEquals("/", createPrompt.getPath());
}
- List after = context.service.getCollections();
+ List after = context.service.getCollections().get();
DBusSignal[] handled = context.prompt.getSignalHandler().getHandled();
Prompt.Completed completed = (Prompt.Completed) handled[0];
if (completed.dismissed) {
@@ -124,7 +124,7 @@ public void searchItems() {
Map attributes = new HashMap();
attributes.put("Attribute1", "Value1");
- Pair, List> response = context.service.searchItems(attributes);
+ Pair, List> response = context.service.searchItems(attributes).get();
List unlocked = toStrings(response.a);
List locked = toStrings(response.b);
@@ -149,14 +149,14 @@ public void unlockCollections() {
ArrayList lockables = new ArrayList();
lockables.add(context.collection.getPath());
- response = context.service.lock(lockables);
+ response = context.service.lock(lockables).get();
log.info(response.toString());
locked = response.a;
assertEquals(1, locked.size());
prompt = response.b;
assertEquals("/", prompt.getPath());
- response = context.service.unlock(lockables);
+ response = context.service.unlock(lockables).get();
log.info(response.toString());
unlocked = response.a;
assertEquals(0, unlocked.size());
@@ -178,16 +178,16 @@ public void unlockItems() {
List locked, unlocked;
ObjectPath prompt;
- List items = context.collection.getItems();
+ List items = context.collection.getItems().get();
- response = context.service.lock(items);
+ response = context.service.lock(items).get();
log.info(response.toString());
locked = response.a;
assertEquals(1, locked.size());
prompt = response.b;
assertEquals("/", prompt.getPath());
- response = context.service.unlock(items);
+ response = context.service.unlock(items).get();
log.info(response.toString());
unlocked = response.a;
assertEquals(0, unlocked.size());
@@ -203,19 +203,18 @@ public void unlockItems() {
@Test
@Disabled
public void lockCommonCollections() throws InterruptedException, NoSuchObject {
+ context.ensureSession();
// lock common collections:
// * alias/default == collection/login
// * collection/login
// * collection/session
- context.ensureSession();
-
- ArrayList objects = new ArrayList();
- objects.add(Static.Convert.toObjectPath(Static.ObjectPaths.DEFAULT_COLLECTION));
- objects.add(Static.Convert.toObjectPath(Static.ObjectPaths.LOGIN_COLLECTION));
- objects.add(Static.Convert.toObjectPath(Static.ObjectPaths.SESSION_COLLECTION));
+ ArrayList collections = new ArrayList();
+ collections.add(Static.Convert.toObjectPath(Static.ObjectPaths.DEFAULT_COLLECTION));
+ collections.add(Static.Convert.toObjectPath(Static.ObjectPaths.LOGIN_COLLECTION));
+ collections.add(Static.Convert.toObjectPath(Static.ObjectPaths.SESSION_COLLECTION));
- Pair, ObjectPath> response = context.service.lock(objects);
+ Pair, ObjectPath> response = context.service.lock(collections).get();
log.info(response.toString());
List locked = response.a;
@@ -226,9 +225,9 @@ public void lockCommonCollections() throws InterruptedException, NoSuchObject {
ObjectPath prompt = response.b;
assertEquals("/", prompt.getPath());
- for (int i = 0; i < objects.size(); i++) {
- List unlock = Arrays.asList(new ObjectPath[]{objects.get(i)});
- response = context.service.unlock(unlock);
+ for (int i = 0; i < collections.size(); i++) {
+ List collection = Arrays.asList(new ObjectPath[]{collections.get(i)});
+ response = context.service.unlock(collection).get();
prompt = response.b;
if (!prompt.getPath().equals("/")) {
context.prompt.await(prompt);
@@ -251,19 +250,17 @@ public void changeLock() {
ObjectPath result;
obj = new ObjectPath("", Static.ObjectPaths.DEFAULT_COLLECTION);
- result = context.service.changeLock(obj);
+ result = context.service.changeLock(obj).get();
log.info(result.toString());
assertTrue(result.getPath().startsWith("/org/freedesktop/secrets/prompt/"));
-
obj = new ObjectPath("", Static.ObjectPaths.LOGIN_COLLECTION);
- result = context.service.changeLock(obj);
+ result = context.service.changeLock(obj).get();
log.info(result.toString());
assertTrue(result.getPath().startsWith("/org/freedesktop/secrets/prompt/"));
-
obj = new ObjectPath("", Static.ObjectPaths.SESSION_COLLECTION);
- result = context.service.changeLock(obj);
+ result = context.service.changeLock(obj).get();
log.info(result.toString());
assertTrue(result.getPath().startsWith("/org/freedesktop/secrets/prompt/"));
}
@@ -272,8 +269,8 @@ public void changeLock() {
public void getSecrets() {
context.ensureItem();
- List items = context.collection.getItems();
- Map result = context.service.getSecrets(items, context.session.getPath());
+ List items = context.collection.getItems().get();
+ Map result = context.service.getSecrets(items, context.session.getPath()).get();
log.info(result.toString());
assertEquals(1, result.size());
@@ -285,20 +282,20 @@ public void readAlias() {
ObjectPath collection;
- collection = context.service.readAlias("default");
+ collection = context.service.readAlias("default").get();
log.info(collection.toString());
assertEquals(Static.ObjectPaths.LOGIN_COLLECTION, collection.getPath(),
"the default alias should point to the login collection");
- collection = context.service.readAlias("login");
+ collection = context.service.readAlias("login").get();
log.info(collection.toString());
assertEquals(Static.ObjectPaths.LOGIN_COLLECTION, collection.getPath());
- collection = context.service.readAlias("session");
+ collection = context.service.readAlias("session").get();
log.info(collection.toString());
assertEquals(Static.ObjectPaths.SESSION_COLLECTION, collection.getPath());
- collection = context.service.readAlias("test");
+ collection = context.service.readAlias("test").get();
log.info(collection.toString());
assertEquals("/", collection.getPath(),
"the test collection should not have an alias");
@@ -313,14 +310,14 @@ public void setAlias() {
// change the default alias to point to the test collection
context.service.setAlias("default", context.collection.getPath());
- collection = context.service.readAlias("default");
+ collection = context.service.readAlias("default").get();
log.info("default: " + collection);
assertEquals(context.collection.getPath().getPath(), collection.getPath());
// repair the default alias
ObjectPath login = Static.Convert.toObjectPath(Static.ObjectPaths.LOGIN_COLLECTION);
context.service.setAlias("default", login);
- collection = context.service.readAlias("default");
+ collection = context.service.readAlias("default").get();
log.info("default: " + collection);
assertEquals(login.getPath(), collection.getPath());
}
@@ -329,7 +326,7 @@ public void setAlias() {
public void getCollections() {
context.ensureCollection();
- List collections = context.service.getCollections();
+ List collections = context.service.getCollections().get();
log.info(Arrays.toString(collections.toArray()));
List cs = toStrings(collections);
diff --git a/src/test/java/de/swiesend/secretservice/integration/StaticTest.java b/src/test/java/de/swiesend/secretservice/integration/StaticTest.java
index 25c1c78..6ed0276 100644
--- a/src/test/java/de/swiesend/secretservice/integration/StaticTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/StaticTest.java
@@ -1,6 +1,7 @@
package de.swiesend.secretservice.integration;
import de.swiesend.secretservice.Static;
+import de.swiesend.secretservice.Static.Utils;
import org.junit.jupiter.api.Test;
import java.math.BigInteger;
@@ -32,25 +33,34 @@ public void testSecondOakleyGroup() {
@Test
public void isNullOrEmptyCharSeq() {
CharSequence nullCharSeq = null;
- assertTrue(Static.isNullOrEmpty(nullCharSeq));
+ assertTrue(Utils.isNullOrEmpty(nullCharSeq));
CharSequence emptyCharSeq = "";
- assertTrue(Static.isNullOrEmpty(emptyCharSeq));
+ assertTrue(Utils.isNullOrEmpty(emptyCharSeq));
CharSequence blankCharSeq = " ";
- assertTrue(Static.isNullOrEmpty(blankCharSeq));
+ assertTrue(Utils.isNullOrEmpty(blankCharSeq));
CharSequence nonEmptyCharSeq = "not empty";
- assertFalse(Static.isNullOrEmpty(nonEmptyCharSeq));
+ assertFalse(Utils.isNullOrEmpty(nonEmptyCharSeq));
}
@Test
public void isNullOrEmptyStr() {
String nullStr = null;
- assertTrue(Static.isNullOrEmpty(nullStr));
+ assertTrue(Utils.isNullOrEmpty(nullStr));
String emptyStr = "";
- assertTrue(Static.isNullOrEmpty(emptyStr));
+ assertTrue(Utils.isNullOrEmpty(emptyStr));
String blankStr = " ";
- assertTrue(Static.isNullOrEmpty(blankStr));
+ assertTrue(Utils.isNullOrEmpty(blankStr));
String nonEmptyStr = "not empty";
- assertFalse(Static.isNullOrEmpty(nonEmptyStr));
+ assertFalse(Utils.isNullOrEmpty(nonEmptyStr));
+ }
+
+ @Test
+ public void isNullOrEmptyArrayOfObjects() {
+ assertTrue(Utils.isNullOrEmpty((Object[]) null));
+ Object[] emptyArrayOfObj = new Object[0];
+ assertTrue(Utils.isNullOrEmpty(emptyArrayOfObj));
+ Object[] nonEmptyArrayOfObj = new Object[]{new Object()};
+ assertFalse(Utils.isNullOrEmpty(nonEmptyArrayOfObj));
}
}
diff --git a/src/test/java/de/swiesend/secretservice/integration/keyring/InternalUnsupportedGuiltRiddenInterfaceTest.java b/src/test/java/de/swiesend/secretservice/integration/keyring/InternalUnsupportedGuiltRiddenInterfaceTest.java
index 8ebb496..4804d0f 100644
--- a/src/test/java/de/swiesend/secretservice/integration/keyring/InternalUnsupportedGuiltRiddenInterfaceTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/keyring/InternalUnsupportedGuiltRiddenInterfaceTest.java
@@ -51,7 +51,7 @@ public void afterEach() throws InterruptedException {
@Test
public void changeWithMasterPassword() throws InterruptedException {
- List collections = service.getCollections();
+ List collections = service.getCollections().get();
List cs = Static.Convert.toStrings(collections);
if (!cs.contains("/org/freedesktop/secrets/collection/test")) {
HashMap properties = new HashMap();
@@ -83,11 +83,11 @@ public void changeWithPrompt() throws InterruptedException {
@Test
public void createWithMasterPassword() throws InterruptedException {
- List collections = service.getCollections();
+ List collections = service.getCollections().get();
List cs = Static.Convert.toStrings(collections);
if (cs.contains("/org/freedesktop/secrets/collection/test")) {
- ObjectPath deleted = collection.delete();
+ ObjectPath deleted = collection.delete().get();
assertEquals("/", deleted.getPath());
Thread.currentThread().sleep(100L); // await signal: Service.CollectionDeleted
}
@@ -97,7 +97,7 @@ public void createWithMasterPassword() throws InterruptedException {
iugri.createWithMasterPassword(properties, original);
Thread.currentThread().sleep(100L); // await signal: Service.CollectionCreated
- collections = service.getCollections();
+ collections = service.getCollections().get();
cs = Static.Convert.toStrings(collections);
assertTrue(cs.contains(Static.ObjectPaths.collection("test")));
diff --git a/src/test/java/de/swiesend/secretservice/integration/test/Context.java b/src/test/java/de/swiesend/secretservice/integration/test/Context.java
index 01b6837..3a85a48 100644
--- a/src/test/java/de/swiesend/secretservice/integration/test/Context.java
+++ b/src/test/java/de/swiesend/secretservice/integration/test/Context.java
@@ -16,6 +16,7 @@
import java.util.Map;
import static java.lang.System.exit;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class Context {
@@ -74,6 +75,8 @@ public void ensureSession() {
}
session = service.getSession();
+ log.info(session.getObjectPath());
+ assertNotNull(session);
assertTrue(session.getObjectPath().startsWith(Static.ObjectPaths.SESSION + "/s"));
password = new Secret(session.getPath(), "".getBytes(), "test".getBytes());
@@ -84,9 +87,9 @@ public void ensureCollection() {
collection = new Collection("test", service);
- collections = Static.Convert.toStrings(service.getCollections());
+ collections = Static.Convert.toStrings(service.getCollections().get());
if (collections.contains(Static.ObjectPaths.collection("test"))) {
- ObjectPath deletePrompt = collection.delete();
+ ObjectPath deletePrompt = collection.delete().get();
if (!deletePrompt.getPath().equals("/")) {
log.error("won't wait for prompt in automated test context.");
exit(-3);
@@ -94,7 +97,7 @@ public void ensureCollection() {
}
Map properties = Collection.createProperties("test");
withoutPrompt.createWithMasterPassword(properties, password);
- collections = Static.Convert.toStrings(service.getCollections());
+ collections = Static.Convert.toStrings(service.getCollections().get());
if (collection.isLocked()) {
withoutPrompt.unlockWithMasterPassword(collection.getPath(), password);
@@ -128,14 +131,14 @@ public void ensureItem() {
withoutPrompt.unlockWithMasterPassword(collection.getPath(), password);
}
- List items = collection.getItems();
+ List items = collection.getItems().get();
for (ObjectPath path : items) {
Item i = new Item(path, service);
i.delete();
}
Map properties = Item.createProperties("TestItem", attributes);
- Pair response = collection.createItem(properties, secret, true);
+ Pair response = collection.createItem(properties, secret, true).get();
ObjectPath itemPath = response.a;
item = new Item(itemPath, service);
From 98b6a20ce7869433e55dbeb1d88097e05796b460 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sat, 2 Apr 2022 15:10:36 +0200
Subject: [PATCH 06/74] Make version 2.0.0-alpha
---
pom.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/pom.xml b/pom.xml
index af7f0eb..4be2b76 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,6 +57,7 @@
UTF-8
17
+ 17
2.0.0
From da5ed3b5c0fea5ae807cae407813ff204414f221 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sat, 2 Apr 2022 15:10:55 +0200
Subject: [PATCH 07/74] Add new functional interfaces
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 4be2b76..80a1856 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,10 +62,10 @@
2.0.0
4.3.0
- 2.0.9
+ 2.0.7
- 5.10.0
+ 5.9.3
From 32fee71c3d05a299c26f449cd905b0ae68f8297f Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sun, 11 Sep 2022 16:51:44 +0200
Subject: [PATCH 08/74] Extend the README
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index c62c1a7..aa1bc6a 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
[![Maven Central](https://img.shields.io/maven-central/v/de.swiesend/secret-service.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22de.swiesend%22%20AND%20a:%22secret-service%22)
-A Java library for storing secrets in a keyring over the D-Bus.
+A _Java_ library for storing secrets in the _Gnome Keyring_ over _DBus_.
-The library is conforming to the freedesktop.org
+The library is conform to the freedesktop.org
[Secret Service API 0.2](https://specifications.freedesktop.org/secret-service/0.2) and thus compatible with Gnome linux systems.
The Secret Service itself is implemented by the [`gnome-keyring`](https://wiki.gnome.org/action/show/Projects/GnomeKeyring) and provided by the [`gnome-keyring-daemon`](https://wiki.gnome.org/Projects/GnomeKeyring/RunningDaemon).
From ca5839d50c843de4d2efef28adf5a0cf979b60ed Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sun, 11 Sep 2022 16:53:42 +0200
Subject: [PATCH 09/74] Align
---
.../simple/SimpleCollection.java | 51 +++++++++----------
1 file changed, 25 insertions(+), 26 deletions(-)
diff --git a/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java b/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
index 0c76b50..f42a05a 100644
--- a/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
+++ b/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
@@ -87,11 +87,11 @@ public SimpleCollection(String label, CharSequence password) throws IOException
try {
encrypted = transport.encrypt(password);
} catch (NoSuchAlgorithmException |
- NoSuchPaddingException |
- InvalidAlgorithmParameterException |
- InvalidKeyException |
- BadPaddingException |
- IllegalBlockSizeException e) {
+ NoSuchPaddingException |
+ InvalidAlgorithmParameterException |
+ InvalidKeyException |
+ BadPaddingException |
+ IllegalBlockSizeException e) {
log.error("Could not establish transport encryption.", e);
}
}
@@ -290,10 +290,10 @@ private void init() throws IOException {
withoutPrompt = new InternalUnsupportedGuiltRiddenInterface(service);
}
} catch (NoSuchAlgorithmException |
- InvalidAlgorithmParameterException |
- InvalidKeySpecException |
- InvalidKeyException |
- DBusException e) {
+ InvalidAlgorithmParameterException |
+ InvalidKeySpecException |
+ InvalidKeyException |
+ DBusException e) {
throw new IOException("Could not initiate transport encryption.", e);
}
}
@@ -381,7 +381,7 @@ private void unlock() {
performPrompt(response.b);
if (!collection.isLocked()) {
isUnlockedOnceWithUserPermission = true;
- log.info("Unlocked collection: " + collection.getLabel() + " (" + collection.getObjectPath() + ")");
+ log.info("Unlocked collection: \"" + collection.getLabel().get() + "\" (" + collection.getObjectPath() + ")");
}
}
}
@@ -487,11 +487,11 @@ public String createItem(String label, CharSequence password, Map new Secret(sessionPath, null))) {
decrypted = transport.decrypt(secret);
} catch (NoSuchPaddingException |
- NoSuchAlgorithmException |
- InvalidAlgorithmParameterException |
- InvalidKeyException |
- BadPaddingException |
- IllegalBlockSizeException e) {
+ NoSuchAlgorithmException |
+ InvalidAlgorithmParameterException |
+ InvalidKeyException |
+ BadPaddingException |
+ IllegalBlockSizeException e) {
log.error("Could not decrypt the secret.", e);
}
return decrypted;
From 9fc24291721f6a1096170c0d6b1e4adf309a980c Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sun, 11 Sep 2022 16:54:05 +0200
Subject: [PATCH 10/74] Only log simple names
---
.../secretservice/handlers/SignalHandler.java | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/main/java/de/swiesend/secretservice/handlers/SignalHandler.java b/src/main/java/de/swiesend/secretservice/handlers/SignalHandler.java
index 93055ce..8e4d265 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/SignalHandler.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/SignalHandler.java
@@ -65,27 +65,27 @@ public void handle(DBusSignal s) {
if (s instanceof Collection.ItemCreated) {
Collection.ItemCreated ic = (Collection.ItemCreated) s;
- log.info("Received signal Collection.ItemCreated: " + ic.item);
+ log.info("Received signal: Collection.ItemCreated(" + ic.item + ")");
} else if (s instanceof Collection.ItemChanged) {
Collection.ItemChanged ic = (Collection.ItemChanged) s;
- log.debug("Received signal Collection.ItemChanged: " + ic.item);
+ log.debug("Received signal: Collection.ItemChanged(" + ic.item + ")");
} else if (s instanceof Collection.ItemDeleted) {
Collection.ItemDeleted ic = (Collection.ItemDeleted) s;
- log.info("Received signal Collection.ItemDeleted: " + ic.item);
+ log.info("Received signal: Collection.ItemDeleted(" + ic.item + ")");
} else if (s instanceof Prompt.Completed) {
Prompt.Completed c = (Prompt.Completed) s;
- log.info("Received signal Prompt.Completed(" + s.getPath() + "): {dismissed: " + c.dismissed + ", result: " + c.result + "}");
+ log.info("Received signal: Prompt.Completed(" + s.getPath() + "): {dismissed: " + c.dismissed + ", result: " + c.result + "}");
} else if (s instanceof Service.CollectionCreated) {
Service.CollectionCreated cc = (Service.CollectionCreated) s;
- log.info("Received signal Service.CollectionCreated: " + cc.collection);
+ log.info("Received signal: Service.CollectionCreated(" + cc.collection + ")");
} else if (s instanceof Service.CollectionChanged) {
Service.CollectionChanged cc = (Service.CollectionChanged) s;
- log.info("Received signal Service.CollectionChanged: " + cc.collection);
+ log.info("Received signal: Service.CollectionChanged(" + cc.collection + ")");
} else if (s instanceof Service.CollectionDeleted) {
Service.CollectionDeleted cc = (Service.CollectionDeleted) s;
- log.info("Received signal Service.CollectionDeleted: " + cc.collection);
+ log.info("Received signal: Service.CollectionDeleted(" + cc.collection + ")");
} else try {
- log.warn("Received unexpected signal: " + s.getClass().toString() + " {" + s.toString() + "}");
+ log.warn("Received unexpected signal: " + s.getClass().getName() + ": {" + s + "}");
} catch (NullPointerException e) {
log.warn("Received unknown signal.");
}
From dfb60d1b796c5a9f7a7b34d1dd9ce7e2031f145e Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Sun, 11 Sep 2022 16:55:49 +0200
Subject: [PATCH 11/74] Enable setProperty() with checked response
---
.../handlers/MessageHandler.java | 37 ++++++++-----------
.../secretservice/handlers/Messaging.java | 2 +-
.../secretservice/integration/ItemTest.java | 4 +-
3 files changed, 20 insertions(+), 23 deletions(-)
diff --git a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
index 32d2166..deea436 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
@@ -24,6 +24,11 @@ public MessageHandler(DBusConnection connection) {
this.connection = connection;
}
+ public MessageHandler(DBusConnection connection, boolean fireAndForget) {
+ this.connection = connection;
+ this.fireAndForget = fireAndForget;
+ }
+
public Optional send(String service, String path, String iface, String method, String signature, Object... args) {
try {
org.freedesktop.dbus.messages.Message message = new MethodCall(
@@ -35,12 +40,13 @@ public Optional send(String service, String path, String iface, String
connection.sendMessage(message);
org.freedesktop.dbus.messages.Message response = ((MethodCall) message).getReply(MAX_DELAY_MILLIS);
- if (log.isTraceEnabled()) log.trace(String.valueOf(response));
+ if (log.isTraceEnabled()) log.trace("Response: " + response);
Object[] parameters = null;
if (response != null) {
parameters = response.getParameters();
- if (log.isDebugEnabled()) log.debug(Arrays.deepToString(parameters));
+ if (log.isDebugEnabled())
+ log.debug("Response parameters for method " + iface + "/" + method + ": " + Arrays.deepToString(parameters));
}
if (response instanceof org.freedesktop.dbus.errors.Error) {
@@ -71,7 +77,7 @@ public Optional send(String service, String path, String iface, String
if (log.isDebugEnabled()) log.debug(error);
return Optional.empty();
default:
- log.error("Unexpected org.freedesktop.dbus.errors.Error: ", error);
+ log.error("Unexpected org.freedesktop.dbus.errors.Error: \"" + error + "\" with parameters: " + Arrays.deepToString(parameters));
return Optional.empty();
}
}
@@ -101,25 +107,14 @@ public Optional getAllProperties(String service, String path, String if
}
public boolean setProperty(String service, String path, String iface, String property, Variant value) {
+ if (log.isDebugEnabled()) log.debug(iface + "@" + property + " with variant: " + value);
Optional maybeResponse = send(service, path, Static.DBus.Interfaces.DBUS_PROPERTIES, "Set", "ssv", iface, property, value);
- // TODO: resolve return value
-// if (maybeResponse.isPresent() && !fireAndForget) {
-// Optional maybeValue = getProperty(service, path, iface, property);
-// if (maybeValue.isPresent()) {
-// Variant result = maybeValue.get();
-// if (result == null) return false;
-// boolean valueCond = value.getValue() == result.getValue();
-// boolean signatureCond = value.getSig() == result.getSig();
-// boolean typeCond = value.getType() == result.getType();
-// return valueCond && signatureCond && typeCond;
-// } else {
-// return false;
-// }
-// } else {
-// return maybeResponse.isPresent();
-// }
- //return true;
- return maybeResponse.isPresent();
+ if (maybeResponse.isPresent() && !fireAndForget) {
+ Optional maybePropertyValue = getProperty(service, path, iface, property);
+ return value.equals(maybePropertyValue.orElse(null));
+ } else {
+ return maybeResponse.isPresent();
+ }
}
}
diff --git a/src/main/java/de/swiesend/secretservice/handlers/Messaging.java b/src/main/java/de/swiesend/secretservice/handlers/Messaging.java
index 1cf5fb9..f9364c0 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/Messaging.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/Messaging.java
@@ -21,7 +21,7 @@ public abstract class Messaging {
public Messaging(DBusConnection connection, List> signals,
String serviceName, String objectPath, String interfaceName) {
this.connection = connection;
- this.msg = new MessageHandler(connection);
+ this.msg = new MessageHandler(connection, true);
if (signals != null) {
this.sh.connect(connection, signals);
}
diff --git a/src/test/java/de/swiesend/secretservice/integration/ItemTest.java b/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
index 9a6924e..7451c54 100644
--- a/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
@@ -16,6 +16,7 @@
import static de.swiesend.secretservice.integration.test.Context.label;
import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
public class ItemTest {
@@ -165,7 +166,8 @@ public void getLabel() {
@Test
public void setLabel() {
- context.item.setLabel("RelabeledItem");
+ boolean result = context.item.setLabel("RelabeledItem");
+ assertTrue(result);
String label = context.item.getLabel().get();
log.info(label("label", label));
assertEquals("RelabeledItem", label);
From e23b3590ce6e65568c3287b795b8a6cf9424fcc4 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Thu, 6 Oct 2022 14:10:45 +0200
Subject: [PATCH 12/74] Add "org.freedesktop.DBus.Error.UnknownObject"
---
.../de/swiesend/secretservice/handlers/MessageHandler.java | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
index deea436..322d915 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
@@ -69,11 +69,12 @@ public Optional send(String service, String path, String iface, String
}
return Optional.empty();
case "org.freedesktop.DBus.Error.NoReply":
- case "org.freedesktop.DBus.Error.UnknownMethod":
case "org.freedesktop.DBus.Error.ServiceUnknown":
- case "org.freedesktop.dbus.exceptions.NotConnected":
+ case "org.freedesktop.DBus.Error.UnknownMethod":
+ case "org.freedesktop.DBus.Error.UnknownObject":
case "org.freedesktop.DBus.Local.Disconnected":
case "org.freedesktop.dbus.exceptions.FatalDBusException":
+ case "org.freedesktop.dbus.exceptions.NotConnected":
if (log.isDebugEnabled()) log.debug(error);
return Optional.empty();
default:
From 19cb9aa1634f631dae0a6b9246c12f672293e1fd Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Thu, 10 Nov 2022 11:26:22 +0100
Subject: [PATCH 13/74] Add subclasses for unambiguous control flow for the
initialization
---
.../secretservice/TransportEncryption.java | 257 ++++++++++--------
.../simple/SimpleCollection.java | 66 ++---
.../integration/IntegrationTest.java | 23 +-
.../integration/test/Context.java | 14 +-
4 files changed, 195 insertions(+), 165 deletions(-)
diff --git a/src/main/java/de/swiesend/secretservice/TransportEncryption.java b/src/main/java/de/swiesend/secretservice/TransportEncryption.java
index 388c46b..240e969 100644
--- a/src/main/java/de/swiesend/secretservice/TransportEncryption.java
+++ b/src/main/java/de/swiesend/secretservice/TransportEncryption.java
@@ -6,6 +6,8 @@
import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.types.Variant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.crypto.*;
import javax.crypto.interfaces.DHPublicKey;
@@ -25,7 +27,8 @@ public class TransportEncryption implements AutoCloseable {
public static final int PRIVATE_VALUE_BITS = 1024;
public static final int AES_BITS = 128;
- private Service service;
+ private static final Logger log = LoggerFactory.getLogger(TransportEncryption.class);
+ private de.swiesend.secretservice.Service service; // TODO: should adhere to the interface
private DHParameterSpec dhParameters = null;
private KeyPair keypair = null;
private PublicKey publicKey = null;
@@ -42,7 +45,7 @@ public TransportEncryption(DBusConnection connection) {
this.service = new Service(connection);
}
- public TransportEncryption(Service service) {
+ public TransportEncryption(de.swiesend.secretservice.Service service) {
this.service = service;
}
@@ -54,145 +57,167 @@ static private int toBytes(int bits) {
return bits / 8;
}
- public void initialize() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+ public Optional initialize() {
- // create dh parameter specification with prime, generator and bits
- BigInteger prime = fromBinary(Static.RFC_2409.SecondOakleyGroup.PRIME);
- BigInteger generator = fromBinary(Static.RFC_2409.SecondOakleyGroup.GENERATOR);
- dhParameters = new DHParameterSpec(prime, generator, PRIVATE_VALUE_BITS);
+ try {
+ // create dh parameter specification with prime, generator and bits
+ BigInteger prime = fromBinary(Static.RFC_2409.SecondOakleyGroup.PRIME);
+ BigInteger generator = fromBinary(Static.RFC_2409.SecondOakleyGroup.GENERATOR);
+ dhParameters = new DHParameterSpec(prime, generator, PRIVATE_VALUE_BITS);
+
+ // generate DH keys from specification
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(Static.Algorithm.DIFFIE_HELLMAN);
+ keyPairGenerator.initialize(dhParameters);
+ keypair = keyPairGenerator.generateKeyPair();
+ publicKey = keypair.getPublic();
+ privateKey = keypair.getPrivate();
+
+ return Optional.of(new InitializedSession());
+ } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
+ log.error("The secret service could not be initialized as the service does not provide the expected transport encryption algorithm.", e);
+ return Optional.empty();
+ }
+ }
- // generate DH keys from specification
- KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(Static.Algorithm.DIFFIE_HELLMAN);
- keyPairGenerator.initialize(dhParameters);
- keypair = keyPairGenerator.generateKeyPair();
- publicKey = keypair.getPublic();
- privateKey = keypair.getPrivate();
+ public Service getService() {
+ return service;
}
- public boolean openSession() throws DBusException {
- if (keypair == null) {
- throw new IllegalStateException("Missing own keypair. Call initialize() first.");
+ public void clear() {
+ if (privateKey != null) try {
+ privateKey.destroy();
+ } catch (DestroyFailedException e) {
+ Secret.clear(privateKey.getEncoded());
}
-
- // The public keys are transferred as an array of bytes representing an unsigned integer of arbitrary size,
- // most-significant byte first (e.g., the integer 32768 is represented as the 2-byte string 0x80 0x00)
- BigInteger ya = ((DHPublicKey) publicKey).getY();
-
- // open session with "Client DH pub key as an array of bytes" without prime or generator
- Optional, ObjectPath>> osResponse = service.openSession(
- Static.Algorithm.DH_IETF1024_SHA256_AES128_CBC_PKCS7, new Variant(ya.toByteArray()));
-
- // transform peer's raw Y to a public key
- if (osResponse.isPresent()) {
- yb = osResponse.get().a.getValue();
- return true;
- } else {
- return false;
+ if (sessionKey != null) try {
+ sessionKey.destroy();
+ } catch (DestroyFailedException e) {
+ Secret.clear(sessionKey.getEncoded());
}
}
- public void generateSessionKey() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
- if (yb == null) {
- throw new IllegalStateException("Missing peer public key. Call openSession() first.");
- }
-
- DHPublicKeySpec dhPublicKeySpec = new DHPublicKeySpec(fromBinary(yb), dhParameters.getP(), dhParameters.getG());
- KeyFactory keyFactory = KeyFactory.getInstance(Static.Algorithm.DIFFIE_HELLMAN);
- DHPublicKey peerPublicKey = (DHPublicKey) keyFactory.generatePublic(dhPublicKeySpec);
-
- KeyAgreement keyAgreement = KeyAgreement.getInstance(Static.Algorithm.DIFFIE_HELLMAN);
- keyAgreement.init(privateKey);
- keyAgreement.doPhase(peerPublicKey, true);
- byte[] rawSessionKey = keyAgreement.generateSecret();
-
- // HKDF digest into a 128-bit key by extract and expand with "NULL salt and empty info"
- // see: https://standards.freedesktop.org/secret-service/0.2/ch07s03.html
- byte[] pseudoRandomKey = HKDF.fromHmacSha256().extract((byte[]) null, rawSessionKey);
- byte[] keyingMaterial = HKDF.fromHmacSha256().expand(pseudoRandomKey, null, toBytes(AES_BITS));
-
- sessionKey = new SecretKeySpec(keyingMaterial, Static.Algorithm.AES);
+ @Override
+ public void close() {
+ clear();
}
- public Secret encrypt(CharSequence plain) throws NoSuchAlgorithmException, NoSuchPaddingException,
- InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
-
- final byte[] bytes = Secret.toBytes(plain);
- try {
- return encrypt(bytes, StandardCharsets.UTF_8);
- } finally {
- Secret.clear(bytes);
+ public class InitializedSession {
+
+ public Optional openSession() {
+ if (keypair == null) {
+ throw new IllegalStateException("Missing own keypair. Call TransportEncryption.initialize() first.");
+ }
+ // The public keys are transferred as an array of bytes representing an unsigned integer of arbitrary size,
+ // most-significant byte first (e.g., the integer 32768 is represented as the 2-byte string 0x80 0x00)
+ BigInteger ya = ((DHPublicKey) publicKey).getY();
+
+ // open session with "Client DH pub key as an array of bytes" without prime or generator
+ return service.openSession(
+ Static.Algorithm.DH_IETF1024_SHA256_AES128_CBC_PKCS7,
+ new Variant(ya.toByteArray())
+ ).flatMap(pair -> {
+ // transform peer's raw Y to a public key
+ yb = pair.a.getValue();
+ return Optional.of(new OpenedSession());
+ });
}
}
- public Secret encrypt(byte[] plain, Charset charset) throws NoSuchAlgorithmException, NoSuchPaddingException,
- InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
-
- if (plain == null) return null;
-
- if (service == null) {
- throw new IllegalStateException("Missing session. Call openSession() first.");
- }
- if (sessionKey == null) {
- throw new IllegalStateException("Missing session key. Call generateSessionKey() first.");
+ public class OpenedSession {
+ public Optional generateSessionKey() {
+ if (yb == null) {
+ throw new IllegalStateException("Missing peer public key. Call Initialized.openSession() first.");
+ }
+ try {
+ DHPublicKeySpec dhPublicKeySpec = new DHPublicKeySpec(fromBinary(yb), dhParameters.getP(), dhParameters.getG());
+ KeyFactory keyFactory = KeyFactory.getInstance(Static.Algorithm.DIFFIE_HELLMAN);
+ DHPublicKey peerPublicKey = (DHPublicKey) keyFactory.generatePublic(dhPublicKeySpec);
+
+ KeyAgreement keyAgreement = KeyAgreement.getInstance(Static.Algorithm.DIFFIE_HELLMAN);
+ keyAgreement.init(privateKey);
+ keyAgreement.doPhase(peerPublicKey, true);
+ byte[] rawSessionKey = keyAgreement.generateSecret();
+
+ // HKDF digest into a 128-bit key by extract and expand with "NULL salt and empty info"
+ // see: https://standards.freedesktop.org/secret-service/0.2/ch07s03.html
+ byte[] pseudoRandomKey = HKDF.fromHmacSha256().extract((byte[]) null, rawSessionKey);
+ byte[] keyingMaterial = HKDF.fromHmacSha256().expand(pseudoRandomKey, null, toBytes(AES_BITS));
+
+ sessionKey = new SecretKeySpec(keyingMaterial, Static.Algorithm.AES);
+
+ if (sessionKey != null) {
+ return Optional.of(new EncryptedSession());
+ } else {
+ return Optional.empty();
+ }
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException e) {
+ log.error("Could not generate a new session key for the current session", e);
+ return Optional.empty();
+ }
}
- // secret.parameter - 16 byte AES initialization vector
- final byte[] salt = new byte[toBytes(AES_BITS)];
- SecureRandom random = SecureRandom.getInstance(Static.Algorithm.SHA1_PRNG);
- random.nextBytes(salt);
- IvParameterSpec ivSpec = new IvParameterSpec(salt);
+ }
- Cipher cipher = Cipher.getInstance(Static.Algorithm.AES_CBC_PKCS5);
- cipher.init(Cipher.ENCRYPT_MODE, sessionKey, ivSpec);
+ public class EncryptedSession {
+ public Secret encrypt(CharSequence plain) throws NoSuchAlgorithmException, NoSuchPaddingException,
+ InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
- String contentType = Secret.createContentType(charset);
+ final byte[] bytes = Secret.toBytes(plain);
+ try {
+ return encrypt(bytes, StandardCharsets.UTF_8);
+ } finally {
+ Secret.clear(bytes);
+ }
+ }
- return new Secret(service.getSession().getPath(), ivSpec.getIV(), cipher.doFinal(plain), contentType);
- }
+ public Secret encrypt(byte[] plain, Charset charset) throws NoSuchAlgorithmException, NoSuchPaddingException,
+ InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
- public char[] decrypt(Secret secret) throws NoSuchPaddingException,
- NoSuchAlgorithmException,
- InvalidAlgorithmParameterException,
- InvalidKeyException,
- BadPaddingException,
- IllegalBlockSizeException {
+ if (plain == null) return null;
- if (secret == null) return null;
+ if (service == null) {
+ throw new IllegalStateException("Missing session. Call Initialized.openSession() first.");
+ }
+ if (sessionKey == null) {
+ throw new IllegalStateException("Missing session key. Call Opened.generateSessionKey() first.");
+ }
- if (sessionKey == null) {
- throw new IllegalStateException("Missing session key. Call generateSessionKey() first.");
- }
+ // secret.parameter - 16 byte AES initialization vector
+ final byte[] salt = new byte[toBytes(AES_BITS)];
+ SecureRandom random = SecureRandom.getInstance(Static.Algorithm.SHA1_PRNG);
+ random.nextBytes(salt);
+ IvParameterSpec ivSpec = new IvParameterSpec(salt);
- IvParameterSpec ivSpec = new IvParameterSpec(secret.getSecretParameters());
- Cipher cipher = Cipher.getInstance(Static.Algorithm.AES_CBC_PKCS5);
- cipher.init(Cipher.DECRYPT_MODE, sessionKey, ivSpec);
- final byte[] decrypted = cipher.doFinal(secret.getSecretValue());
- try {
- return Secret.toChars(decrypted);
- } finally {
- Secret.clear(decrypted);
- }
- }
+ Cipher cipher = Cipher.getInstance(Static.Algorithm.AES_CBC_PKCS5);
+ cipher.init(Cipher.ENCRYPT_MODE, sessionKey, ivSpec);
- public Service getService() {
- return service;
- }
+ String contentType = Secret.createContentType(charset);
- public void clear() {
- if (privateKey != null) try {
- privateKey.destroy();
- } catch (DestroyFailedException e) {
- Secret.clear(privateKey.getEncoded());
+ return new Secret(service.getSession().getPath(), ivSpec.getIV(), cipher.doFinal(plain), contentType);
}
- if (sessionKey != null) try {
- sessionKey.destroy();
- } catch (DestroyFailedException e) {
- Secret.clear(sessionKey.getEncoded());
- }
- }
- @Override
- public void close() {
- clear();
+ public char[] decrypt(Secret secret) throws NoSuchPaddingException,
+ NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException,
+ InvalidKeyException,
+ BadPaddingException,
+ IllegalBlockSizeException {
+
+ if (secret == null) return null;
+
+ if (sessionKey == null) {
+ throw new IllegalStateException("Missing session key. Call Opened.generateSessionKey() first.");
+ }
+
+ IvParameterSpec ivSpec = new IvParameterSpec(secret.getSecretParameters());
+ Cipher cipher = Cipher.getInstance(Static.Algorithm.AES_CBC_PKCS5);
+ cipher.init(Cipher.DECRYPT_MODE, sessionKey, ivSpec);
+ final byte[] decrypted = cipher.doFinal(secret.getSecretValue());
+ try {
+ return Secret.toChars(decrypted);
+ } finally {
+ Secret.clear(decrypted);
+ }
+ }
}
}
diff --git a/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java b/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
index f42a05a..91364b9 100644
--- a/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
+++ b/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
@@ -8,6 +8,7 @@
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.interfaces.DBus;
import org.freedesktop.dbus.types.Variant;
+import de.swiesend.secretservice.Collection;
import de.swiesend.secretservice.interfaces.Prompt.Completed;
import de.swiesend.secretservice.gnome.keyring.InternalUnsupportedGuiltRiddenInterface;
import org.slf4j.Logger;
@@ -21,12 +22,8 @@
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
-import java.security.spec.InvalidKeySpecException;
import java.time.Duration;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.RejectedExecutionException;
import static de.swiesend.secretservice.Static.DBus.DEFAULT_DELAY_MILLIS;
@@ -39,6 +36,7 @@ public final class SimpleCollection extends de.swiesend.secretservice.simple.int
private static final Logger log = LoggerFactory.getLogger(SimpleCollection.class);
private static final DBusConnection connection = getConnection();
private static final Thread shutdownHook = setupShutdownHook();
+ private TransportEncryption.EncryptedSession transportEncryptedSession = null;
private TransportEncryption transport = null;
private Service service = null;
private Session session = null;
@@ -85,7 +83,7 @@ public SimpleCollection(String label, CharSequence password) throws IOException
init();
if (password != null) {
try {
- encrypted = transport.encrypt(password);
+ encrypted = transportEncryptedSession.encrypt(password);
} catch (NoSuchAlgorithmException |
NoSuchPaddingException |
InvalidAlgorithmParameterException |
@@ -190,18 +188,20 @@ public static boolean isAvailable() {
// algorithm (DH_IETF1024_SHA256_AES128_CBC_PKCS7) or raises an error, like
// "org.freedesktop.DBus.Error.ServiceUnknown <: org.freedesktop.dbus.exceptions.DBusException"
TransportEncryption transport = new TransportEncryption(connection);
- transport.initialize();
- boolean isSessionSupported = transport.openSession();
+ Optional opened = transport.initialize().flatMap(init -> init.openSession());
+ boolean isSessionSupported = opened.isPresent();
transport.close();
return isSessionSupported;
} catch (DBusException | ExceptionInInitializerError e) {
log.warn("The secret service is not available. You may want to install the `gnome-keyring` package. Is the `gnome-keyring-daemon` running?", e);
return false;
- } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
+ }
+ // TODO: remove comment
+ /*catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
log.error("The secret service could not be initialized as the service does not provide the expected transport encryption algorithm.", e);
return false;
- }
+ }*/
} else {
log.error("No D-Bus connection: Cannot check if all needed services are available.");
return false;
@@ -278,24 +278,18 @@ private static Thread setupShutdownHook() {
private void init() throws IOException {
if (!isAvailable()) throw new IOException("The secret service is not available.");
- try {
- transport = new TransportEncryption(connection);
- transport.initialize();
- transport.openSession();
- transport.generateSessionKey();
- service = transport.getService();
- session = service.getSession();
- prompt = new Prompt(service);
- if (isGnomeKeyringAvailable()) {
- withoutPrompt = new InternalUnsupportedGuiltRiddenInterface(service);
- }
- } catch (NoSuchAlgorithmException |
- InvalidAlgorithmParameterException |
- InvalidKeySpecException |
- InvalidKeyException |
- DBusException e) {
- throw new IOException("Could not initiate transport encryption.", e);
- }
+ TransportEncryption transport = new TransportEncryption(connection);
+ transportEncryptedSession = transport
+ .initialize()
+ .flatMap(i -> i.openSession())
+ .flatMap(o -> o.generateSessionKey())
+ .orElseThrow(
+ () -> new IOException("Could not initiate transport encryption.")
+ );
+ service = transport.getService();
+ session = service.getSession();
+ prompt = new Prompt(service);
+ withoutPrompt = new InternalUnsupportedGuiltRiddenInterface(service);
}
private Map getLabels() {
@@ -362,7 +356,7 @@ private List lockable() {
public void lock() {
if (collection != null && !collection.isLocked()) {
service.lock(lockable());
- log.info("Locked collection: " + collection.getLabel() + " (" + collection.getObjectPath() + ")");
+ log.info("Locked collection: \"" + collection.getLabel().get() + "\" (" + collection.getObjectPath() + ")");
try {
Thread.currentThread().sleep(DEFAULT_DELAY_MILLIS);
} catch (InterruptedException e) {
@@ -373,16 +367,16 @@ public void lock() {
private void unlock() {
if (collection != null && collection.isLocked()) {
- if (withoutPrompt != null && encrypted != null) {
- withoutPrompt.unlockWithMasterPassword(collection.getPath(), encrypted);
- log.debug("Unlocked collection: " + collection.getLabel() + " (" + collection.getObjectPath() + ")");
- } else {
+ if (encrypted == null || isDefault()) {
Pair, ObjectPath> response = service.unlock(lockable()).get();
performPrompt(response.b);
if (!collection.isLocked()) {
isUnlockedOnceWithUserPermission = true;
log.info("Unlocked collection: \"" + collection.getLabel().get() + "\" (" + collection.getObjectPath() + ")");
}
+ } else {
+ withoutPrompt.unlockWithMasterPassword(collection.getPath(), encrypted);
+ log.debug("Unlocked collection: \"" + collection.getLabel().get() + "\" (" + collection.getObjectPath() + ")");
}
}
}
@@ -475,7 +469,7 @@ public String createItem(String label, CharSequence password, Map properties = Item.createProperties(label, attributes);
- try (final Secret secret = transport.encrypt(password)) {
+ try (final Secret secret = transportEncryptedSession.encrypt(password)) {
Pair response = collection.createItem(properties, secret, false).get();
if (response == null) return null;
item = response.a;
@@ -543,7 +537,7 @@ public void updateItem(String objectPath, String label, CharSequence password, M
item.setAttributes(attributes);
}
- if (password != null) try (Secret secret = transport.encrypt(password)) {
+ if (password != null) try (Secret secret = transportEncryptedSession.encrypt(password)) {
item.setSecret(secret);
} catch (NoSuchAlgorithmException |
NoSuchPaddingException |
@@ -619,7 +613,7 @@ public char[] getSecret(String objectPath) {
char[] decrypted = null;
ObjectPath sessionPath = session.getPath();
try (final Secret secret = item.getSecret(sessionPath).orElseGet(() -> new Secret(sessionPath, null))) {
- decrypted = transport.decrypt(secret);
+ decrypted = transportEncryptedSession.decrypt(secret);
} catch (NoSuchPaddingException |
NoSuchAlgorithmException |
InvalidAlgorithmParameterException |
diff --git a/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java b/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
index 3890f46..2457332 100644
--- a/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
@@ -13,6 +13,7 @@
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.security.auth.DestroyFailedException;
+import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -37,20 +38,26 @@ public void testWithTransportEncryption() throws
NoSuchPaddingException,
InvalidKeyException,
BadPaddingException,
- IllegalBlockSizeException, InterruptedException, InvalidKeySpecException, DestroyFailedException {
+ IllegalBlockSizeException,
+ InterruptedException,
+ IOException {
TransportEncryption transportEncryption = new TransportEncryption();
- transportEncryption.initialize();
- transportEncryption.openSession();
- transportEncryption.generateSessionKey();
+ TransportEncryption.EncryptedSession encryptedSession = transportEncryption
+ .initialize()
+ .flatMap(i -> i.openSession())
+ .flatMap(o -> o.generateSessionKey())
+ .orElseThrow(
+ () -> new IOException("Could not initiate transport encryption.")
+ );
String plain = "super secret";
- Secret encrypted = transportEncryption.encrypt(plain);
+ Secret encrypted = encryptedSession.encrypt(plain);
byte[] encBase64 = Base64.getEncoder().encode(encrypted.getSecretValue());
log.info(label("encrypted secret (base64)", new String(encBase64)));
- char[] decrypted = transportEncryption.decrypt(encrypted);
+ char[] decrypted = encryptedSession.decrypt(encrypted);
log.info(label(" decrypted secret", new String(decrypted)));
assertEquals(plain, new String(decrypted));
@@ -58,7 +65,7 @@ public void testWithTransportEncryption() throws
Session session = service.getSession();
InternalUnsupportedGuiltRiddenInterface noPrompt = new InternalUnsupportedGuiltRiddenInterface(service);
- Secret master = transportEncryption.encrypt("test");
+ Secret master = encryptedSession.encrypt("test");
Collection collection = new Collection("test", service);
List collections = Static.Convert.toStrings(service.getCollections().get());
@@ -97,7 +104,7 @@ public void testWithTransportEncryption() throws
assertEquals(encrypted.getSession(), actual.getSession());
assertEquals(encrypted.getContentType(), actual.getContentType());
- decrypted = transportEncryption.decrypt(actual);
+ decrypted = encryptedSession.decrypt(actual);
log.info(label(" decrypted remote secret", new String(decrypted)));
assertEquals(plain, new String(decrypted));
diff --git a/src/test/java/de/swiesend/secretservice/integration/test/Context.java b/src/test/java/de/swiesend/secretservice/integration/test/Context.java
index 3a85a48..8d845a2 100644
--- a/src/test/java/de/swiesend/secretservice/integration/test/Context.java
+++ b/src/test/java/de/swiesend/secretservice/integration/test/Context.java
@@ -9,6 +9,7 @@
import de.swiesend.secretservice.gnome.keyring.InternalUnsupportedGuiltRiddenInterface;
import org.slf4j.Logger;
+import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
@@ -24,7 +25,7 @@ public class Context {
public Logger log;
public boolean encrypted;
- public TransportEncryption encryption = null;
+ public TransportEncryption.EncryptedSession encryption = null;
public Service service = null;
public Session session = null;
public Secret password = null;
@@ -62,10 +63,13 @@ public void ensureSession() {
try {
if (encrypted) {
- encryption = new TransportEncryption(service);
- encryption.initialize();
- encryption.openSession();
- encryption.generateSessionKey();
+ encryption = new TransportEncryption(service)
+ .initialize()
+ .flatMap(i -> i.openSession())
+ .flatMap(o -> o.generateSessionKey())
+ .orElseThrow(
+ () -> new IOException("Could not initiate transport encryption.")
+ );
} else {
service.openSession(Static.Algorithm.PLAIN, new Variant(""));
}
From 252a2d88dc69c4b17aeec976e78f3f166d865b47 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Thu, 10 Nov 2022 11:32:23 +0100
Subject: [PATCH 14/74] Shorten type
---
src/main/java/de/swiesend/secretservice/Service.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/de/swiesend/secretservice/Service.java b/src/main/java/de/swiesend/secretservice/Service.java
index 903dc5b..288b6fd 100644
--- a/src/main/java/de/swiesend/secretservice/Service.java
+++ b/src/main/java/de/swiesend/secretservice/Service.java
@@ -26,7 +26,7 @@ public Service(DBusConnection connection) {
public Optional, ObjectPath>> openSession(String algorithm, Variant input) {
return send("OpenSession", "sv", algorithm, input)
.filter(response -> !Static.Utils.isNullOrEmpty(response) && response.length == 2)
- .flatMap(response -> Optional.of(new Pair, ObjectPath>((Variant) response[0], (ObjectPath) response[1])))
+ .flatMap(response -> Optional.of(new Pair<>((Variant) response[0], (ObjectPath) response[1])))
.map(pair -> {
log.debug("Got session: " + pair.b.getPath());
session = new Session(pair.b, this);
From 4e014ddc0a9b5c6727c0ff9f96f91237e71b923f Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Thu, 10 Nov 2022 11:33:35 +0100
Subject: [PATCH 15/74] Implement the new secret service 2.0
---
.../secret/functional/SecretService.java | 199 ++++++++++++++++++
1 file changed, 199 insertions(+)
create mode 100644 src/main/java/de/swiesend/secret/functional/SecretService.java
diff --git a/src/main/java/de/swiesend/secret/functional/SecretService.java b/src/main/java/de/swiesend/secret/functional/SecretService.java
new file mode 100644
index 0000000..6183729
--- /dev/null
+++ b/src/main/java/de/swiesend/secret/functional/SecretService.java
@@ -0,0 +1,199 @@
+package de.swiesend.secret.functional;
+
+import de.swiesend.secret.functional.interfaces.ServiceInterface;
+import de.swiesend.secret.functional.interfaces.SessionInterface;
+import de.swiesend.secret.functional.interfaces.SystemInterface;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.freedesktop.dbus.interfaces.DBus;
+import org.freedesktop.secret.Pair;
+import org.freedesktop.secret.Service;
+import org.freedesktop.secret.Static;
+import org.freedesktop.secret.TransportEncryption;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+import java.util.*;
+
+
+enum Activatable {
+ DBUS(Static.DBus.Service.DBUS),
+ SECRETS(Static.Service.SECRETS),
+ GNOME_KEYRING(org.gnome.keyring.Static.Service.KEYRING);
+
+ public final String name;
+
+ Activatable(String name) {
+ this.name = name;
+ }
+
+}
+
+class AvailableServices {
+
+ private static final Logger log = LoggerFactory.getLogger(AvailableServices.class);
+
+ public EnumSet services = EnumSet.noneOf(Activatable.class);
+
+ public AvailableServices(System system) {
+ DBusConnection connection = system.getConnection();
+ if (connection.isConnected()) {
+ try {
+ DBus bus = connection.getRemoteObject(
+ Static.DBus.Service.DBUS,
+ Static.DBus.ObjectPaths.DBUS,
+ DBus.class);
+ List activatableServices = Arrays.asList(bus.ListActivatableNames());
+
+ if (!activatableServices.contains(Static.DBus.Service.DBUS)) {
+ log.error("Missing D-Bus service: " + Static.DBus.Service.DBUS);
+ } else {
+ services.add(Activatable.DBUS);
+ }
+
+ if (!activatableServices.contains(Static.Service.SECRETS)) {
+ log.error("Missing D-Bus service: " + Static.Service.SECRETS);
+ } else {
+ services.add(Activatable.SECRETS);
+ }
+ if (!activatableServices.contains(org.gnome.keyring.Static.Service.KEYRING)) {
+ log.warn("Proceeding without D-Bus service: " + org.gnome.keyring.Static.Service.KEYRING);
+ } else {
+ services.add(Activatable.GNOME_KEYRING);
+ }
+ } catch (DBusException | ExceptionInInitializerError e) {
+ log.warn("The secret service is not available. You may want to install the `gnome-keyring` package. Is the `gnome-keyring-daemon` running?", e);
+ }
+ }
+ }
+
+
+}
+
+public class SecretService extends ServiceInterface {
+
+ private static final Logger log = LoggerFactory.getLogger(SecretService.class);
+ private Map sessions = new HashMap<>();
+ private org.freedesktop.secret.Service service = null;
+
+ // TODO: remove unnecessary fields
+ // private Prompt prompt = null;
+ // private InternalUnsupportedGuiltRiddenInterface withoutPrompt = null;
+ // private org.freedesktop.secret.Session session = null;
+ private boolean isOrgGnomeKeyringAvailable = false;
+
+ private SecretService(SystemInterface system, AvailableServices available) {
+ this.service = new Service(system.getConnection());
+ this.isOrgGnomeKeyringAvailable = available.services.contains(Activatable.GNOME_KEYRING);
+ }
+
+ /**
+ * Create a Secret-Service instance with initialized transport encryption.
+ */
+ public static Optional create() {
+ return System.connect()
+ .map(system -> new Pair<>(system, new AvailableServices(system)))
+ .filter(pair -> isAvailable(pair.a, pair.b))
+ .map(pair -> new SecretService(pair.a, pair.b));
+ }
+
+ /**
+ * Checks if all necessary D-Bus services are provided by the system:
+ * org.freedesktop.DBus
+ * org.freedesktop.secrets
+ * org.gnome.keyring
+ *
+ * @return true if the secret service is available, otherwise false and will log an error message.
+ */
+ private static boolean isAvailable(System system, AvailableServices available) {
+ DBusConnection connection = system.getConnection();
+ if (connection.isConnected()) {
+ try {
+ if (!available.services.contains(Activatable.DBUS)) {
+ log.error("Missing D-Bus service: " + Activatable.DBUS.name);
+ return false;
+ }
+ if (!available.services.contains(Activatable.SECRETS)) {
+ log.error("Missing D-Bus service: " + Activatable.SECRETS.name);
+ return false;
+ }
+ if (!available.services.contains(Activatable.GNOME_KEYRING)) {
+ log.warn("Proceeding without D-Bus service: " + Activatable.GNOME_KEYRING.name);
+ }
+
+ // The following calls intent to open a session without actually generating a full session.
+ // Necessary in order to check if the provided 'secret service' supports the expected transport
+ // encryption algorithm (DH_IETF1024_SHA256_AES128_CBC_PKCS7) or raises an error, like
+ // "org.freedesktop.DBus.Error.ServiceUnknown <: org.freedesktop.dbus.exceptions.DBusException"
+ TransportEncryption transport = new TransportEncryption(connection);
+ boolean isSessionSupported = transport
+ .initialize()
+ .flatMap(init -> init.openSession())
+ .isPresent();
+ transport.close();
+
+ return isSessionSupported;
+ } catch (ExceptionInInitializerError e) {
+ log.warn("The secret service is not available. " +
+ "You may want to install the `gnome-keyring` package. Is the `gnome-keyring-daemon` running?", e);
+ return false;
+ }
+ } else {
+ log.error("No D-Bus connection: Cannot check if all needed services are available.");
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isOrgGnomeKeyringAvailable() {
+ return isOrgGnomeKeyringAvailable;
+ }
+
+ @Override
+ public boolean clear() {
+ return false;
+ }
+
+ @Override
+ public Optional openSession() {
+ return Session.open(this);
+ }
+
+ public void registerSession(Session session) {
+ this.sessions.put(session.getId(), session);
+ }
+
+ public void unregisterSession(Session session) {
+ this.sessions.remove(session.getId());
+ }
+
+ @Override
+ public List getSessions() {
+ return this.sessions.values().stream().toList();
+ }
+
+ /*@Override
+ public SystemInterface getSystem() {
+ return this.system;
+ }*/
+
+ @Override
+ public Duration getTimeout() {
+ return null;
+ }
+
+ @Override
+ public void setTimeout(Duration timeout) {
+
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+
+ public Service getService() {
+ return service;
+ }
+}
From 9d02040ffc4d47f9208bc7dd41ce0dca1b60e87c Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Thu, 10 Nov 2022 11:35:30 +0100
Subject: [PATCH 16/74] Add interfaces and implementations
---
.../secret/functional/Collection.java | 298 ++++++++++++++++++
.../swiesend/secret/functional/Session.java | 91 ++++++
.../de/swiesend/secret/functional/System.java | 66 ++++
.../interfaces/SystemInterface.java | 26 ++
.../secret/functional/SecretServiceTest.java | 34 ++
5 files changed, 515 insertions(+)
create mode 100644 src/main/java/de/swiesend/secret/functional/Collection.java
create mode 100644 src/main/java/de/swiesend/secret/functional/Session.java
create mode 100644 src/main/java/de/swiesend/secret/functional/System.java
create mode 100644 src/main/java/de/swiesend/secret/functional/interfaces/SystemInterface.java
create mode 100644 src/test/java/de/swiesend/secret/functional/SecretServiceTest.java
diff --git a/src/main/java/de/swiesend/secret/functional/Collection.java b/src/main/java/de/swiesend/secret/functional/Collection.java
new file mode 100644
index 0000000..f4706a7
--- /dev/null
+++ b/src/main/java/de/swiesend/secret/functional/Collection.java
@@ -0,0 +1,298 @@
+package de.swiesend.secret.functional;
+
+import de.swiesend.secret.functional.interfaces.CollectionInterface;
+import de.swiesend.secret.functional.interfaces.ServiceInterface;
+import de.swiesend.secret.functional.interfaces.SessionInterface;
+import org.freedesktop.dbus.DBusPath;
+import org.freedesktop.dbus.ObjectPath;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.types.Variant;
+import org.freedesktop.secret.*;
+import org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
+import java.util.*;
+
+import static org.freedesktop.secret.Static.DEFAULT_PROMPT_TIMEOUT;
+
+public class Collection implements CollectionInterface {
+
+ private static final Logger log = LoggerFactory.getLogger(Collection.class);
+ /*this.prompt = new Prompt(service);
+ this.withoutPrompt = new InternalUnsupportedGuiltRiddenInterface(service);
+ */
+ org.freedesktop.secret.Collection collection = null;
+ SessionInterface session = null;
+ ServiceInterface service = null;
+ DBusConnection connection = null;
+ private Duration timeout = DEFAULT_PROMPT_TIMEOUT;
+ private Boolean isUnlockedOnceWithUserPermission = false;
+ private String label = null;
+ private Secret encrypted = null;
+ private Prompt prompt = null;
+
+ public Collection(SessionInterface session, String label, CharSequence password) {
+ init(session);
+ this.label = label;
+ try {
+ this.encrypted = session.getEncryptedSession().encrypt(password);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchPaddingException e) {
+ throw new RuntimeException(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new RuntimeException(e);
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException(e);
+ } catch (BadPaddingException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalBlockSizeException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Collection(SessionInterface session) {
+ init(session);
+ }
+
+ private void init(SessionInterface session) {
+ this.session = session;
+ ObjectPath path = Static.Convert.toObjectPath(Static.ObjectPaths.DEFAULT_COLLECTION);
+ collection = new org.freedesktop.secret.Collection(path, session.getService().getService());
+ prompt = new Prompt(session.getService().getService());
+ log.info(collection.getId());
+ this.service = session.getService();
+ this.connection = service.getService().getConnection();
+ }
+
+ @Override
+ public boolean clear() {
+ return false;
+ }
+
+ @Override
+ public Optional createItem(String label, CharSequence password) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional createItem(String label, CharSequence password, Map attributes) {
+ if (Static.Utils.isNullOrEmpty(password)) {
+ throw new IllegalArgumentException("The password may not be null or empty.");
+ }
+ if (label == null) {
+ throw new IllegalArgumentException("The label of the item may not be null.");
+ }
+
+ if (collection == null || session.getEncryptedSession() == null) return Optional.empty();
+
+ unlock();
+
+ DBusPath item = null;
+ final Map properties = Item.createProperties(label, attributes);
+ try (final Secret secret = session.getEncryptedSession().encrypt(password)) {
+ Pair response = collection.createItem(properties, secret, false).get();
+ if (response == null) return null;
+ item = response.a;
+ if ("/".equals(item.getPath())) {
+ org.freedesktop.secret.interfaces.Prompt.Completed completed = prompt.await(response.b);
+ if (!completed.dismissed) {
+ org.freedesktop.secret.Collection.ItemCreated ic = collection
+ .getSignalHandler()
+ .getLastHandledSignal(org.freedesktop.secret.Collection.ItemCreated.class);
+ item = ic.item;
+ }
+ }
+ } catch (NoSuchAlgorithmException |
+ NoSuchPaddingException |
+ InvalidAlgorithmParameterException |
+ InvalidKeyException |
+ BadPaddingException |
+ IllegalBlockSizeException e) {
+ log.error("Cloud not encrypt the secret.", e);
+ }
+
+ if (null != item) {
+ return Optional.of(item.getPath());
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public boolean delete() {
+ return false;
+ }
+
+ @Override
+ public boolean deleteItem(String objectPath) {
+ return false;
+ }
+
+ @Override
+ public boolean deleteItems(List objectPaths) {
+ return false;
+ }
+
+ @Override
+ public Optional> getAttributes(String objectPath) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional> getItems(Map attributes) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getLabel(String objectPath) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional setLabel(String objectPath) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getSecret(String objectPath) {
+ if (Static.Utils.isNullOrEmpty(objectPath)) return Optional.empty();
+ unlock();
+
+ final Item item = getItem(objectPath);
+
+ char[] decrypted = null;
+ ObjectPath sessionPath = session.getSession().getPath();
+ try (final Secret secret = item.getSecret(sessionPath).orElseGet(() -> new Secret(sessionPath, null))) {
+ decrypted = session.getEncryptedSession().decrypt(secret);
+ } catch (NoSuchPaddingException |
+ NoSuchAlgorithmException |
+ InvalidAlgorithmParameterException |
+ InvalidKeyException |
+ BadPaddingException |
+ IllegalBlockSizeException e) {
+ log.error("Could not decrypt the secret.", e);
+ return Optional.empty();
+ }
+ if (decrypted == null) {
+ return Optional.empty();
+ } else {
+ return Optional.of(decrypted);
+ }
+ }
+
+ @Override
+ public Optional> getSecrets() {
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean isLocked() {
+ return false;
+ }
+
+ @Override
+ public boolean lock() {
+ return false;
+ }
+
+ private void unlock() {
+ if (collection != null && collection.isLocked()) {
+ if (encrypted == null || isDefault()) {
+ Pair, ObjectPath> response = service.getService().unlock(lockable()).get();
+ performPrompt(response.b);
+ if (!collection.isLocked()) {
+ isUnlockedOnceWithUserPermission = true;
+ log.info("Unlocked collection: \"" + collection.getLabel().get() + "\" (" + collection.getObjectPath() + ")");
+ }
+ } else if (service.isOrgGnomeKeyringAvailable()) {
+ InternalUnsupportedGuiltRiddenInterface withoutPrompt = new InternalUnsupportedGuiltRiddenInterface(service.getService());
+ withoutPrompt.unlockWithMasterPassword(collection.getPath(), encrypted);
+ log.debug("Unlocked collection: \"" + collection.getLabel().get() + "\" (" + collection.getObjectPath() + ")");
+ }
+ }
+ }
+
+ @Override
+ public boolean unlockWithUserPermission() {
+ return false;
+ }
+
+ @Override
+ public boolean updateItem(String objectPath, String label, CharSequence password, Map attributes) {
+ return false;
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+
+ private Map getLabels() {
+ List collections = service.getService().getCollections().get();
+
+ Map labels = new HashMap();
+ for (ObjectPath path : collections) {
+ org.freedesktop.secret.Collection c = new org.freedesktop.secret.Collection(path, service.getService(), null);
+ labels.put(path, c.getLabel().get());
+ }
+
+ return labels;
+ }
+
+ private boolean exists(String label) {
+ Map labels = getLabels();
+ return labels.containsValue(label);
+ }
+
+ private ObjectPath getCollectionPath(String label) {
+ Map labels = getLabels();
+
+ ObjectPath path = null;
+ for (Map.Entry entry : labels.entrySet()) {
+ ObjectPath p = entry.getKey();
+ String l = entry.getValue();
+ if (label.equals(l)) {
+ path = p;
+ break;
+ }
+ }
+ return path;
+ }
+
+ private boolean isDefault() {
+ if (connection != null && connection.isConnected()) {
+ List defaults = Arrays.asList(null, "login", "session", "default");
+ return defaults.contains(collection.getId());
+ } else {
+ log.error("No D-Bus connection: Cannot check if the collection is the default collection.");
+ return false;
+ }
+ }
+
+ private void performPrompt(ObjectPath path) {
+ if (!("/".equals(path.getPath()))) {
+ prompt.await(path, timeout);
+ }
+ }
+
+ private Item getItem(String path) {
+ if (path != null) {
+ return new Item(Static.Convert.toObjectPath(path), service.getService());
+ } else {
+ return null;
+ }
+ }
+
+ private List lockable() {
+ return Arrays.asList(collection.getPath());
+ }
+}
diff --git a/src/main/java/de/swiesend/secret/functional/Session.java b/src/main/java/de/swiesend/secret/functional/Session.java
new file mode 100644
index 0000000..70ba2d3
--- /dev/null
+++ b/src/main/java/de/swiesend/secret/functional/Session.java
@@ -0,0 +1,91 @@
+package de.swiesend.secret.functional;
+
+import de.swiesend.secret.functional.interfaces.ServiceInterface;
+import org.freedesktop.secret.TransportEncryption;
+import de.swiesend.secret.functional.interfaces.CollectionInterface;
+import de.swiesend.secret.functional.interfaces.SessionInterface;
+import org.freedesktop.secret.Service;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+import java.util.UUID;
+
+public class Session implements SessionInterface {
+
+ private static final Logger log = LoggerFactory.getLogger(Session.class);
+
+ @Override
+ public TransportEncryption.EncryptedSession getEncryptedSession() {
+ return encryptedSession;
+ }
+
+ public TransportEncryption.EncryptedSession encryptedSession = null;
+
+ private UUID id = null;
+
+ @Override
+ public org.freedesktop.secret.Session getSession() {
+ return session;
+ }
+
+ org.freedesktop.secret.Session session = null;
+
+ @Override
+ public ServiceInterface getService() {
+ return service;
+ }
+
+ private ServiceInterface service = null;
+
+ private Session(ServiceInterface service, TransportEncryption.EncryptedSession encryptedSession) {
+ this.id = UUID.randomUUID();
+ this.service = service;
+ this.encryptedSession = encryptedSession;
+ this.session = service.getService().getSession();
+ }
+
+ public static Optional open(ServiceInterface service) {
+
+ Service dbusService = service.getService();
+
+ return new TransportEncryption(dbusService)
+ .initialize()
+ .flatMap(initialized -> initialized.openSession())
+ .flatMap(opened -> opened.generateSessionKey())
+ .map(encryptedSession -> {
+ Session session = new Session(service, encryptedSession);
+ service.registerSession(session);
+ return session;
+ })
+ .or(() -> {
+ log.error("Could not open transport encrypted session.");
+ return Optional.empty();
+ });
+ }
+
+ @Override
+ public boolean clear() {
+ // TODO: to be implemented
+ return false;
+ }
+
+ @Override
+ public Optional collection(String label, CharSequence password) {
+ return Optional.of(new Collection(this, label, password));
+ }
+
+ @Override
+ public Optional defaultCollection() {
+ return Optional.of(new Collection(this));
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ public UUID getId() {
+ return id;
+ }
+}
diff --git a/src/main/java/de/swiesend/secret/functional/System.java b/src/main/java/de/swiesend/secret/functional/System.java
new file mode 100644
index 0000000..18b7284
--- /dev/null
+++ b/src/main/java/de/swiesend/secret/functional/System.java
@@ -0,0 +1,66 @@
+package de.swiesend.secret.functional;
+
+import de.swiesend.secret.functional.interfaces.SystemInterface;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+public class System extends SystemInterface {
+
+ private static final Logger log = LoggerFactory.getLogger(System.class);
+
+ private static DBusConnection connection = null;
+
+ private System(DBusConnection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * Try to get a new DBus connection.
+ *
+ * @return a new DBusConnection or Optional.empty()
+ */
+ public static Optional connect() {
+ try {
+ DBusConnection dbus = DBusConnection.newConnection(DBusConnection.DBusBusType.SESSION);
+ System c = new System(dbus);
+ return Optional.of(c);
+ } catch (DBusException e) {
+ if (e == null) {
+ log.warn("Could not communicate properly with the D-Bus.");
+ } else {
+ log.warn(String.format("Could not communicate properly with the D-Bus: [%s]: %s", e.getClass().getSimpleName(), e.getMessage()));
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static boolean isConnected() {
+ if (connection == null) {
+ return false;
+ } else {
+ return connection.isConnected();
+ }
+ }
+
+ @Override
+ public DBusConnection getConnection() {
+ return connection;
+ }
+
+ synchronized public boolean disconnect() {
+ connection.disconnect();
+ return connection.isConnected();
+ }
+
+ /*private static Optional setupShutdownHook() {
+ return Optional.empty();
+ }*/
+ @Override
+ public void close() throws Exception {
+ connection.close();
+ }
+}
diff --git a/src/main/java/de/swiesend/secret/functional/interfaces/SystemInterface.java b/src/main/java/de/swiesend/secret/functional/interfaces/SystemInterface.java
new file mode 100644
index 0000000..7f95bb9
--- /dev/null
+++ b/src/main/java/de/swiesend/secret/functional/interfaces/SystemInterface.java
@@ -0,0 +1,26 @@
+package de.swiesend.secret.functional.interfaces;
+
+import de.swiesend.secret.functional.System;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+public abstract class SystemInterface implements AutoCloseable {
+
+ private static final Logger log = LoggerFactory.getLogger(SystemInterface.class);
+
+ public static Optional connect() {
+ log.warn("Do not call the interface method, but the implementation.");
+ return null;
+ }
+
+ synchronized public boolean disconnect() {
+ log.warn("Do not call the interface method, but the implementation.");
+ return false;
+ }
+
+ abstract public DBusConnection getConnection();
+
+}
diff --git a/src/test/java/de/swiesend/secret/functional/SecretServiceTest.java b/src/test/java/de/swiesend/secret/functional/SecretServiceTest.java
new file mode 100644
index 0000000..5fae466
--- /dev/null
+++ b/src/test/java/de/swiesend/secret/functional/SecretServiceTest.java
@@ -0,0 +1,34 @@
+package de.swiesend.secret.functional;
+
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SecretServiceTest {
+
+ private static final Logger log = LoggerFactory.getLogger(SecretServiceTest.class);
+
+ @Test
+ @DisplayName("Fiddling around")
+ public void fiddlingAround() throws DBusException {
+
+ char[] password = SecretService.create().flatMap(
+ service -> service.openSession().flatMap(
+ session -> session.collection("test", "test").flatMap(
+ collection -> collection.createItem("label", "password", Map.of("key", "value")).flatMap(
+ path -> collection.getSecret(path)
+ )
+ )
+ )
+ ).get();
+
+ assertEquals("password", new String(password));
+ }
+
+}
From b690696be1c6d583a3265d3759bfba7484ece0a2 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Mon, 16 Jan 2023 19:23:48 +0100
Subject: [PATCH 17/74] Add autoclose
---
.../secret/functional/Collection.java | 5 +-
.../secret/functional/SecretService.java | 31 ++++++--
.../swiesend/secret/functional/Session.java | 67 ++++++++++-------
.../de/swiesend/secretservice/Service.java | 9 ++-
.../secretservice/TransportEncryption.java | 17 ++++-
.../handlers/MessageHandler.java | 2 +
.../simple/SimpleCollection.java | 2 +-
.../secret/functional/SecretServiceTest.java | 70 ++++++++++++++---
.../integration/IntegrationTest.java | 2 +-
.../secretservice/integration/ItemTest.java | 28 +++++--
...alUnsupportedGuiltRiddenInterfaceTest.java | 12 ++-
.../integration/test/Context.java | 75 ++++++++++++-------
12 files changed, 221 insertions(+), 99 deletions(-)
diff --git a/src/main/java/de/swiesend/secret/functional/Collection.java b/src/main/java/de/swiesend/secret/functional/Collection.java
index f4706a7..1ab28bc 100644
--- a/src/main/java/de/swiesend/secret/functional/Collection.java
+++ b/src/main/java/de/swiesend/secret/functional/Collection.java
@@ -65,6 +65,7 @@ public Collection(SessionInterface session) {
private void init(SessionInterface session) {
this.session = session;
+ // TODO: do not always refer to the default collection
ObjectPath path = Static.Convert.toObjectPath(Static.ObjectPaths.DEFAULT_COLLECTION);
collection = new org.freedesktop.secret.Collection(path, session.getService().getService());
prompt = new Prompt(session.getService().getService());
@@ -233,7 +234,9 @@ public boolean updateItem(String objectPath, String label, CharSequence password
@Override
public void close() throws Exception {
-
+ // TODO: remove log.info
+ log.info("collection close is triggered");
+ if (this.encrypted != null) this.encrypted.close();
}
private Map getLabels() {
diff --git a/src/main/java/de/swiesend/secret/functional/SecretService.java b/src/main/java/de/swiesend/secret/functional/SecretService.java
index 6183729..3aac555 100644
--- a/src/main/java/de/swiesend/secret/functional/SecretService.java
+++ b/src/main/java/de/swiesend/secret/functional/SecretService.java
@@ -15,6 +15,7 @@
import java.time.Duration;
import java.util.*;
+import java.util.Collection;
enum Activatable {
@@ -81,11 +82,13 @@ public class SecretService extends ServiceInterface {
// private Prompt prompt = null;
// private InternalUnsupportedGuiltRiddenInterface withoutPrompt = null;
// private org.freedesktop.secret.Session session = null;
- private boolean isOrgGnomeKeyringAvailable = false;
+
+ private boolean gnomeKeyringAvailable = false;
+
private SecretService(SystemInterface system, AvailableServices available) {
this.service = new Service(system.getConnection());
- this.isOrgGnomeKeyringAvailable = available.services.contains(Activatable.GNOME_KEYRING);
+ this.gnomeKeyringAvailable = available.services.contains(Activatable.GNOME_KEYRING);
}
/**
@@ -147,7 +150,7 @@ private static boolean isAvailable(System system, AvailableServices available) {
@Override
public boolean isOrgGnomeKeyringAvailable() {
- return isOrgGnomeKeyringAvailable;
+ return this.gnomeKeyringAvailable;
}
@Override
@@ -156,15 +159,21 @@ public boolean clear() {
}
@Override
- public Optional openSession() {
- return Session.open(this);
+ public Optional openSession() {
+ Optional session = Session
+ .open(this)
+ .map(s -> {
+ registerSession(s);
+ return s;
+ });
+ return session;
}
- public void registerSession(Session session) {
+ private void registerSession(SessionInterface session) {
this.sessions.put(session.getId(), session);
}
- public void unregisterSession(Session session) {
+ private void unregisterSession(SessionInterface session) {
this.sessions.remove(session.getId());
}
@@ -190,7 +199,13 @@ public void setTimeout(Duration timeout) {
@Override
public void close() throws Exception {
-
+ // TODO: remove log.info
+ log.info("service close is triggered");
+ List values = this.sessions.values().stream().toList();
+ for (SessionInterface session : values) {
+ unregisterSession(session);
+ session.close();
+ }
}
public Service getService() {
diff --git a/src/main/java/de/swiesend/secret/functional/Session.java b/src/main/java/de/swiesend/secret/functional/Session.java
index 70ba2d3..7e2a646 100644
--- a/src/main/java/de/swiesend/secret/functional/Session.java
+++ b/src/main/java/de/swiesend/secret/functional/Session.java
@@ -1,51 +1,34 @@
package de.swiesend.secret.functional;
-import de.swiesend.secret.functional.interfaces.ServiceInterface;
-import org.freedesktop.secret.TransportEncryption;
import de.swiesend.secret.functional.interfaces.CollectionInterface;
+import de.swiesend.secret.functional.interfaces.ServiceInterface;
import de.swiesend.secret.functional.interfaces.SessionInterface;
import org.freedesktop.secret.Service;
+import org.freedesktop.secret.TransportEncryption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class Session implements SessionInterface {
private static final Logger log = LoggerFactory.getLogger(Session.class);
-
- @Override
- public TransportEncryption.EncryptedSession getEncryptedSession() {
- return encryptedSession;
- }
-
public TransportEncryption.EncryptedSession encryptedSession = null;
+ private List collections = new ArrayList<>();
private UUID id = null;
-
- @Override
- public org.freedesktop.secret.Session getSession() {
- return session;
- }
-
- org.freedesktop.secret.Session session = null;
-
- @Override
- public ServiceInterface getService() {
- return service;
- }
-
private ServiceInterface service = null;
private Session(ServiceInterface service, TransportEncryption.EncryptedSession encryptedSession) {
this.id = UUID.randomUUID();
this.service = service;
this.encryptedSession = encryptedSession;
- this.session = service.getService().getSession();
}
- public static Optional open(ServiceInterface service) {
+ public static Optional open(ServiceInterface service) {
Service dbusService = service.getService();
@@ -55,8 +38,7 @@ public static Optional open(ServiceInterface service) {
.flatMap(opened -> opened.generateSessionKey())
.map(encryptedSession -> {
Session session = new Session(service, encryptedSession);
- service.registerSession(session);
- return session;
+ return (SessionInterface) session;
})
.or(() -> {
log.error("Could not open transport encrypted session.");
@@ -64,6 +46,26 @@ public static Optional open(ServiceInterface service) {
});
}
+ @Override
+ public TransportEncryption.EncryptedSession getEncryptedSession() {
+ return encryptedSession;
+ }
+
+ @Override
+ public org.freedesktop.secret.Session getSession() {
+ return this.encryptedSession.getSession();
+ }
+
+ @Override
+ public List getCollections() {
+ return this.collections;
+ }
+
+ @Override
+ public ServiceInterface getService() {
+ return service;
+ }
+
@Override
public boolean clear() {
// TODO: to be implemented
@@ -72,19 +74,26 @@ public boolean clear() {
@Override
public Optional collection(String label, CharSequence password) {
- return Optional.of(new Collection(this, label, password));
+ CollectionInterface collection = new Collection(this, label, password);
+ this.collections.add(collection);
+ return Optional.of(collection);
}
@Override
public Optional defaultCollection() {
- return Optional.of(new Collection(this));
+ CollectionInterface collection = new Collection(this);
+ this.collections.add(collection);
+ return Optional.of(collection);
}
@Override
- public void close() {
-
+ public void close() throws Exception {
+ for (CollectionInterface collection : this.collections) {
+ collection.close();
+ }
}
+ @Override
public UUID getId() {
return id;
}
diff --git a/src/main/java/de/swiesend/secretservice/Service.java b/src/main/java/de/swiesend/secretservice/Service.java
index 288b6fd..341c7b1 100644
--- a/src/main/java/de/swiesend/secretservice/Service.java
+++ b/src/main/java/de/swiesend/secretservice/Service.java
@@ -13,7 +13,7 @@ public class Service extends Messaging implements de.swiesend.secretservice.inte
public static final List> signals = Arrays.asList(
CollectionCreated.class, CollectionChanged.class, CollectionDeleted.class);
private static final Logger log = LoggerFactory.getLogger(Service.class);
- private Session session = null;
+ // private Session session = null;
public Service(DBusConnection connection) {
super(connection, signals,
@@ -29,7 +29,8 @@ public Optional, ObjectPath>> openSession(String algorithm,
.flatMap(response -> Optional.of(new Pair<>((Variant) response[0], (ObjectPath) response[1])))
.map(pair -> {
log.debug("Got session: " + pair.b.getPath());
- session = new Session(pair.b, this);
+ // TODO: remove session creation from service
+ // session = new Session(pair.b, this);
return pair;
});
}
@@ -117,8 +118,8 @@ public String getObjectPath() {
return Static.ObjectPaths.SECRETS;
}
- public Session getSession() {
+ /*public Session getSession() {
return session;
- }
+ }*/
}
diff --git a/src/main/java/de/swiesend/secretservice/TransportEncryption.java b/src/main/java/de/swiesend/secretservice/TransportEncryption.java
index 240e969..b6d3a98 100644
--- a/src/main/java/de/swiesend/secretservice/TransportEncryption.java
+++ b/src/main/java/de/swiesend/secretservice/TransportEncryption.java
@@ -36,6 +36,8 @@ public class TransportEncryption implements AutoCloseable {
private SecretKey sessionKey = null;
private byte[] yb = null;
+ private Session session = null;
+
public TransportEncryption() throws DBusException {
DBusConnection connection = DBusConnectionBuilder.forSessionBus().withShared(false).build();
this.service = new Service(connection);
@@ -83,6 +85,10 @@ public Service getService() {
return service;
}
+ public Session getSession() {
+ return session;
+ }
+
public void clear() {
if (privateKey != null) try {
privateKey.destroy();
@@ -99,6 +105,7 @@ public void clear() {
@Override
public void close() {
clear();
+ if (session != null) session.close();
}
public class InitializedSession {
@@ -118,6 +125,9 @@ public Optional openSession() {
).flatMap(pair -> {
// transform peer's raw Y to a public key
yb = pair.a.getValue();
+
+ ObjectPath sessionPath = pair.b;
+ session = new Session(sessionPath, service);
return Optional.of(new OpenedSession());
});
}
@@ -159,6 +169,11 @@ public Optional generateSessionKey() {
}
public class EncryptedSession {
+
+ public Session getSession() {
+ return session;
+ }
+
public Secret encrypt(CharSequence plain) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
@@ -193,7 +208,7 @@ public Secret encrypt(byte[] plain, Charset charset) throws NoSuchAlgorithmExcep
String contentType = Secret.createContentType(charset);
- return new Secret(service.getSession().getPath(), ivSpec.getIV(), cipher.doFinal(plain), contentType);
+ return new Secret(session.getPath(), ivSpec.getIV(), cipher.doFinal(plain), contentType);
}
public char[] decrypt(Secret secret) throws NoSuchPaddingException,
diff --git a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
index 322d915..598b7a9 100644
--- a/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
+++ b/src/main/java/de/swiesend/secretservice/handlers/MessageHandler.java
@@ -72,6 +72,8 @@ public Optional send(String service, String path, String iface, String
case "org.freedesktop.DBus.Error.ServiceUnknown":
case "org.freedesktop.DBus.Error.UnknownMethod":
case "org.freedesktop.DBus.Error.UnknownObject":
+ case "org.freedesktop.DBus.Error.InvalidArgs":
+ log.error(error + ": " + Arrays.deepToString(parameters));
case "org.freedesktop.DBus.Local.Disconnected":
case "org.freedesktop.dbus.exceptions.FatalDBusException":
case "org.freedesktop.dbus.exceptions.NotConnected":
diff --git a/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java b/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
index 91364b9..079562c 100644
--- a/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
+++ b/src/main/java/de/swiesend/secretservice/simple/SimpleCollection.java
@@ -287,7 +287,7 @@ private void init() throws IOException {
() -> new IOException("Could not initiate transport encryption.")
);
service = transport.getService();
- session = service.getSession();
+ session = transport.getSession();
prompt = new Prompt(service);
withoutPrompt = new InternalUnsupportedGuiltRiddenInterface(service);
}
diff --git a/src/test/java/de/swiesend/secret/functional/SecretServiceTest.java b/src/test/java/de/swiesend/secret/functional/SecretServiceTest.java
index 5fae466..7827170 100644
--- a/src/test/java/de/swiesend/secret/functional/SecretServiceTest.java
+++ b/src/test/java/de/swiesend/secret/functional/SecretServiceTest.java
@@ -1,6 +1,8 @@
package de.swiesend.secret.functional;
-import org.freedesktop.dbus.exceptions.DBusException;
+import de.swiesend.secret.functional.interfaces.CollectionInterface;
+import de.swiesend.secret.functional.interfaces.ServiceInterface;
+import de.swiesend.secret.functional.interfaces.SessionInterface;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
@@ -16,19 +18,63 @@ public class SecretServiceTest {
@Test
@DisplayName("Fiddling around")
- public void fiddlingAround() throws DBusException {
-
- char[] password = SecretService.create().flatMap(
- service -> service.openSession().flatMap(
- session -> session.collection("test", "test").flatMap(
- collection -> collection.createItem("label", "password", Map.of("key", "value")).flatMap(
- path -> collection.getSecret(path)
- )
- )
- )
- ).get();
+ public void fiddlingAround() throws Exception {
+ char[] password;
+ try (ServiceInterface service = SecretService.create().get()) {
+ CollectionInterface collection = service.openSession().flatMap(session ->
+ session.collection("test", "test")).get();
+ Map attributes = Map.of("key", "value");
+ String item = collection.createItem("label", "password", attributes).get();
+ password = collection.getSecret(item).get();
+ /*for (String i : collection.getItems(attributes).get()) {
+ log.info("[" + collection.getLabel(item).get() + "]" + i);
+ // collection.deleteItem(i);
+ }*/
+ // collection.delete();
+
+ CollectionInterface col2 = service.openSession().flatMap(session ->
+ session.collection("test", "test")).get();
+ }
+ assertEquals("password", new String(password));
+ password = "".toCharArray();
+
+ /*try (ServiceInterface service = SecretService.create().get()) {
+ try (SessionInterface session = service.openSession().get()) {
+ try (CollectionInterface collection = session.collection("test", "test").get()) {
+ String item = collection.createItem("label", "password", Map.of("key", "value")).get();
+ Optional secret = collection.getSecret(item);
+ collection.delete();
+ password = secret.get();
+ }
+ }
+ }
assertEquals("password", new String(password));
+ password = "".toCharArray();
+
+ password = SecretService.create().flatMap(
+ service -> service.openSession()).flatMap(
+ session -> session.collection("test", "test")).flatMap(
+ collection -> {
+ String item = collection.createItem("label", "password", Map.of("key", "value")).get();
+ Optional secret = collection.getSecret(item);
+ collection.delete();
+ return secret;
+ }).get();
+
+ assertEquals("password", new String(password));*/
+
+
+ /*try (ServiceInterface service = SecretService.create().get()){
+ try (SessionInterface session = service.openSession().get()) {
+ try (CollectionInterface collection = session.collection("test", "test").get()) {
+ String item = collection.createItem("label", "password", Map.of("key", "value")).get();
+ Optional secret = collection.getSecret(item);
+ collection.delete();
+ secret;
+ }
+ }
+ }*/
}
}
diff --git a/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java b/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
index 2457332..c5554db 100644
--- a/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/IntegrationTest.java
@@ -62,7 +62,7 @@ public void testWithTransportEncryption() throws
assertEquals(plain, new String(decrypted));
Service service = transportEncryption.getService();
- Session session = service.getSession();
+ Session session = transportEncryption.getSession();
InternalUnsupportedGuiltRiddenInterface noPrompt = new InternalUnsupportedGuiltRiddenInterface(service);
Secret master = encryptedSession.encrypt("test");
diff --git a/src/test/java/de/swiesend/secretservice/integration/ItemTest.java b/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
index 7451c54..dfb5c33 100644
--- a/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/ItemTest.java
@@ -9,14 +9,19 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static de.swiesend.secretservice.integration.test.Context.label;
import static org.junit.jupiter.api.Assertions.*;
-import static org.junit.jupiter.api.Assertions.assertEquals;
public class ItemTest {
@@ -58,9 +63,19 @@ public void getSecret() {
String parameters = new String(secret.getSecretParameters(), StandardCharsets.UTF_8);
log.info(label("parameters", parameters));
- assertEquals("", parameters);
-
- String value = new String(secret.getSecretValue(), StandardCharsets.UTF_8);
+ if (context.encrypted == false) assertEquals("", parameters);
+
+ String value;
+ if (context.encrypted == false) {
+ value = new String(secret.getSecretValue(), StandardCharsets.UTF_8);
+ } else {
+ try {
+ value = new String(context.encryption.decrypt(secret));
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException |
+ InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
+ throw new RuntimeException(e);
+ }
+ }
log.info(label("value", value));
assertEquals("super secret", value);
}
@@ -79,8 +94,9 @@ public void getForeignSecret() {
Collection login = new Collection(alias, context.service);
List items = login.getItems().get();
Item item = new Item(items.get(0), context.service);
- Secret secret = item.getSecret(context.service.getSession().getPath()).get();
- log.info(new String(secret.getSecretValue()));
+ Secret secret = item.getSecret(context.session.getPath()).get();
+ log.info("'" + new String(secret.getSecretValue()) + "' [" + new String(secret.getSecretParameters()) + "]");
+
}
@Test
diff --git a/src/test/java/de/swiesend/secretservice/integration/keyring/InternalUnsupportedGuiltRiddenInterfaceTest.java b/src/test/java/de/swiesend/secretservice/integration/keyring/InternalUnsupportedGuiltRiddenInterfaceTest.java
index 4804d0f..6226044 100644
--- a/src/test/java/de/swiesend/secretservice/integration/keyring/InternalUnsupportedGuiltRiddenInterfaceTest.java
+++ b/src/test/java/de/swiesend/secretservice/integration/keyring/InternalUnsupportedGuiltRiddenInterfaceTest.java
@@ -1,15 +1,12 @@
package de.swiesend.secretservice.integration.keyring;
+import de.swiesend.secretservice.*;
import de.swiesend.secretservice.gnome.keyring.InternalUnsupportedGuiltRiddenInterface;
import org.freedesktop.dbus.ObjectPath;
import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.types.Variant;
-import de.swiesend.secretservice.Collection;
-import de.swiesend.secretservice.Secret;
-import de.swiesend.secretservice.Service;
-import de.swiesend.secretservice.Static;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
@@ -35,10 +32,11 @@ public class InternalUnsupportedGuiltRiddenInterfaceTest {
public void beforeEach() throws DBusException {
connection = DBusConnectionBuilder.forSessionBus().withShared(false).build();
service = new Service(connection);
- service.openSession(Static.Algorithm.PLAIN, new Variant(""));
+ Pair, ObjectPath> pair = service.openSession(Static.Algorithm.PLAIN, new Variant("")).get();
+ ObjectPath sessionPath = pair.b;
iugri = new InternalUnsupportedGuiltRiddenInterface(service);
- original = new Secret(service.getSession().getPath(), "".getBytes(), "test".getBytes());
- master = new Secret(service.getSession().getPath(), "".getBytes(), "master-secret".getBytes());
+ original = new Secret(sessionPath, "".getBytes(), "test".getBytes());
+ master = new Secret(sessionPath, "".getBytes(), "master-secret".getBytes());
collection = new Collection("test", service);
}
diff --git a/src/test/java/de/swiesend/secretservice/integration/test/Context.java b/src/test/java/de/swiesend/secretservice/integration/test/Context.java
index 8d845a2..4c45658 100644
--- a/src/test/java/de/swiesend/secretservice/integration/test/Context.java
+++ b/src/test/java/de/swiesend/secretservice/integration/test/Context.java
@@ -9,8 +9,14 @@
import de.swiesend.secretservice.gnome.keyring.InternalUnsupportedGuiltRiddenInterface;
import org.slf4j.Logger;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -37,7 +43,7 @@ public class Context {
public Context(Logger log) {
this.log = log;
- this.encrypted = false;
+ this.encrypted = true;
}
public Context(Logger log, boolean encrypted) {
@@ -45,6 +51,30 @@ public Context(Logger log, boolean encrypted) {
this.encrypted = encrypted;
}
+ public static String label(String name, String msg) {
+ return name + ": \"" + msg + "\"";
+ }
+
+ public static String label(String name, int number) {
+ return name + ": " + number;
+ }
+
+ public static String label(String name, long number) {
+ return name + ": " + number;
+ }
+
+ public static String label(String name, BigInteger number) {
+ return name + ": " + number;
+ }
+
+ public static String label(String name, byte[] bytes) {
+ return name + ": " + Arrays.toString(bytes);
+ }
+
+ public static String label(String name, List list) {
+ return name + ": " + Arrays.toString(list.toArray());
+ }
+
public void ensureService() {
DBusConnection connection = null;
try {
@@ -58,7 +88,7 @@ public void ensureService() {
prompt = new Prompt(service);
}
- public void ensureSession() {
+ public void ensureSession() throws RuntimeException {
ensureService();
try {
@@ -70,20 +100,31 @@ public void ensureSession() {
.orElseThrow(
() -> new IOException("Could not initiate transport encryption.")
);
+ session = encryption.getSession();
} else {
- service.openSession(Static.Algorithm.PLAIN, new Variant(""));
+ Pair, ObjectPath> pair = service.openSession(Static.Algorithm.PLAIN, new Variant("")).get();
+ session = new Session(pair.b, service);
}
} catch (Exception e) {
log.error("Could not establish transport encryption.", e);
exit(-2);
}
- session = service.getSession();
log.info(session.getObjectPath());
assertNotNull(session);
assertTrue(session.getObjectPath().startsWith(Static.ObjectPaths.SESSION + "/s"));
- password = new Secret(session.getPath(), "".getBytes(), "test".getBytes());
+ String test = "test";
+ if (encrypted) {
+ try {
+ password = encryption.encrypt(test);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException |
+ InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ password = new Secret(session.getPath(), "".getBytes(), test.getBytes());
+ }
}
public void ensureCollection() {
@@ -157,29 +198,5 @@ public void after() {
}
}
- public static String label(String name, String msg) {
- return name + ": \"" + msg + "\"";
- }
-
- public static String label(String name, int number) {
- return name + ": " + number;
- }
-
- public static String label(String name, long number) {
- return name + ": " + number;
- }
-
- public static String label(String name, BigInteger number) {
- return name + ": " + number;
- }
-
- public static String label(String name, byte[] bytes) {
- return name + ": " + Arrays.toString(bytes);
- }
-
- public static String label(String name, List list) {
- return name + ": " + Arrays.toString(list.toArray());
- }
-
}
From 40a3f1cf8bcff57a59b57a5703424040787e3fbd Mon Sep 17 00:00:00 2001
From: Sebastian Wiesendahl
Date: Mon, 26 Jun 2023 10:08:04 +0200
Subject: [PATCH 18/74] Change Messaging Interface to returning `Optional`
---
.../secret/functional/Collection.java | 2 +-
.../de/swiesend/secretservice/Collection.java | 13 +--
.../de/swiesend/secretservice/Service.java | 7 --
.../de/swiesend/secretservice/Static.java | 4 +
.../secretservice/TransportEncryption.java | 74 +++++++++------
.../simple/SimpleCollection.java | 92 +++++++++----------
.../integration/CollectionTest.java | 20 ++--
.../integration/IntegrationTest.java | 12 +--
.../secretservice/integration/ItemTest.java | 2 +-
.../secretservice/integration/PromptTest.java | 2 +-
.../integration/ServiceTest.java | 2 +-
...alUnsupportedGuiltRiddenInterfaceTest.java | 2 +-
.../integration/test/Context.java | 15 +--
13 files changed, 124 insertions(+), 123 deletions(-)
diff --git a/src/main/java/de/swiesend/secret/functional/Collection.java b/src/main/java/de/swiesend/secret/functional/Collection.java
index 1ab28bc..e404009 100644
--- a/src/main/java/de/swiesend/secret/functional/Collection.java
+++ b/src/main/java/de/swiesend/secret/functional/Collection.java
@@ -118,7 +118,7 @@ public Optional createItem(String label, CharSequence password, Map> signals) {
- super(service.getConnection(), signals,
+ public Collection(DBusPath path, DBusConnection connection, List> signals) {
+ super(connection, signals,
Static.Service.SECRETS,
path.getPath(),
Static.Interfaces.COLLECTION);
@@ -34,8 +35,8 @@ public Collection(DBusPath path, Service service, List> signals = Arrays.asList(
CollectionCreated.class, CollectionChanged.class, CollectionDeleted.class);
private static final Logger log = LoggerFactory.getLogger(Service.class);
- // private Session session = null;
public Service(DBusConnection connection) {
super(connection, signals,
@@ -29,8 +28,6 @@ public Optional