]> nmode's Git Repositories - signal-cli/commitdiff
Dbus identities (#1259)
authorAdimarantis <joerg.bublath@intel.com>
Mon, 5 Jun 2023 17:26:00 +0000 (19:26 +0200)
committerGitHub <noreply@github.com>
Mon, 5 Jun 2023 17:26:00 +0000 (19:26 +0200)
* Dbus Identities and Trust

* Update src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java

* PR feedback updates

* Documentation and consistent case sensitivity

* doc for listIdentities and getIdentity

man/signal-cli-dbus.5.adoc
src/main/java/org/asamk/Signal.java
src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java

index 46b11734d2cc297816cc3e3a9b45a06a944e56eb..0f11ef9d4c7a3bef26be23e22d38a9db4e74e8cf 100644 (file)
@@ -416,6 +416,24 @@ Only works if sent from a secondary device.
 
 Exceptions: Failure
 
+==== Identity related methods
+
+listIdentities() -> identities<a(oss)>::
+* identities : Array of structs (objectPath, id, name)
+** objectPath : DBusPath representing the identity object path
+** uuid : Internal uuid of the identity
+** number : Phone number of the identity (or uuid if not known)
+
+Lists all know identities
+
+getIdentity(Number<s>) -> identityPath<o>::
+* Number : Phone number 
+* identityPath : DBusPath object for the identity
+
+Gets the identity Dbus path for a given phone number
+
+Exceptions: Failure
+
 === Signal.Group interface
 
 The following methods listen to the group's object path, which can be obtained from the listGroups() method and is constructed as follows:
@@ -531,6 +549,42 @@ removeDevice() -> <>::
 
 Exceptions: Failure
 
+=== Signal.Identity interface
+
+The following methods listen to the Identities object path, which is constructed as follows:
+
+<ACCOUNT_PATH> + "/Identities/" + identity
+
+identity : Either the phone number of a contact with underscore (_) replacing plus (+) , or if not known its uuid
+
+Identities have the following (case-sensitive) properties:
+
+* Number<s> (read-only) : Phone number of the contact
+* Uuid<x> (read-only) : Internal uuid representing the contact
+* Fingerprint<x> (read-only) : Byte array representing the fingerprint
+* SafetyNumber<s> (read-only) : String representation of the safety number used to verify trust
+* TrustLevel<s> (read-only) : Current trust level (UNSTRUSTED, TRUSTED_UNVERIFIED, TRUSTED_VERIFIED)
+* AddedDate<x> (read-only) : Long representing the number of milliseconds since the Unix epoch
+* ScannableSafetyNumber<x> (read-only) : Byte array representation of the safety number
+
+To get a property, use (replacing `--session` with `--system` if needed):
+`dbus-send --session --dest=org.asamk.Signal --print-reply $OBJECT_PATH org.freedesktop.DBus.Properties.Get string:org.asamk.Signal.Identity string:$PROPERTY_NAME`
+
+To get all properties, use:
+`dbus-send --session --dest=org.asamk.Signal --print-reply $OBJECT_PATH org.freedesktop.DBus.Properties.GetAll string:org.asamk.Signal.Identity`
+
+trust() -> <>::
+
+Establish trust with the given identity. TrustLevel will become TRUSTED_UNVERFIED
+
+Exceptions: Failure
+
+trustVerified(SafetyNumber<s>) -> <>::
+
+Establish trust with the given identity using their safety number. TrustLevel will become TRUSTED_VERIFIED
+
+Exceptions: Failure
+
 === Signal.Configuration interface
 
 The configuration's object path, which exists only for primary devices, is constructed as follows:
index bc8bf74e47cfeea0b074259ea99588fe582f621b..2ca23dfbc808263b9845052f9fa859abdc73db33 100644 (file)
@@ -138,6 +138,10 @@ public interface Signal extends DBusInterface {
 
     DBusPath getDevice(long deviceId);
 
+    public DBusPath getIdentity(String number);
+    
+    public List<StructIdentity> listIdentities();
+
     List<StructDevice> listDevices() throws Error.Failure;
 
     DBusPath getThisDevice();
@@ -551,6 +555,50 @@ public interface Signal extends DBusInterface {
         void enableLink(boolean requiresApproval) throws Error.Failure;
     }
 
+    class StructIdentity extends Struct {
+
+        @Position(0)
+        DBusPath objectPath;
+
+        @Position(1)
+        String uuid;
+
+        @Position(2)
+        String name;
+
+        public StructIdentity(final DBusPath objectPath, final String uuid, final String name) {
+            this.objectPath = objectPath;
+            this.uuid = uuid;
+            this.name = name;
+        }
+
+        public DBusPath getObjectPath() {
+            return objectPath;
+        }
+
+        public String getUuid() {
+            return uuid;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    @DBusProperty(name = "Number", type = String.class, access = DBusProperty.Access.READ)
+    @DBusProperty(name = "Uuid", type = String.class, access = DBusProperty.Access.READ)
+    @DBusProperty(name = "Fingerprint", type = Byte[].class, access = DBusProperty.Access.READ)
+    @DBusProperty(name = "SafetyNumber", type = String.class, access = DBusProperty.Access.READ)
+    @DBusProperty(name = "TrustLevel", type = String.class, access = DBusProperty.Access.READ)
+    @DBusProperty(name = "AddedDate", type = Integer.class, access = DBusProperty.Access.READ)
+    @DBusProperty(name = "ScannableSafetyNumber", type = Byte[].class, access = DBusProperty.Access.READ)
+    interface Identity extends DBusInterface, Properties {
+
+        void trust() throws Error.Failure;
+
+        void trustVerified(String safetyNumber) throws Error.Failure;
+    }
+
     interface Error {
 
         class AttachmentInvalid extends DBusExecutionException {
index b1d648869e6e59b83920422f210002208f68d0fb..6a44ff979c533732b2c4ca0e675e1ab20cbda92a 100644 (file)
@@ -30,6 +30,7 @@ import org.asamk.signal.manager.api.UnregisteredRecipientException;
 import org.asamk.signal.manager.api.UpdateGroup;
 import org.asamk.signal.manager.api.UpdateProfile;
 import org.asamk.signal.manager.api.UserStatus;
+import org.asamk.signal.manager.api.IdentityVerificationCode;
 import org.asamk.signal.util.SendMessageResultUtils;
 import org.freedesktop.dbus.DBusPath;
 import org.freedesktop.dbus.connections.impl.DBusConnection;
@@ -55,6 +56,7 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.UUID;
 
 import static org.asamk.signal.dbus.DbusUtils.makeValidObjectPathElement;
 
@@ -68,6 +70,7 @@ public class DbusSignalImpl implements Signal {
     private DBusPath thisDevice;
     private final List<StructDevice> devices = new ArrayList<>();
     private final List<StructGroup> groups = new ArrayList<>();
+    private final List<StructIdentity> identities = new ArrayList<>();
     private DbusReceiveMessageHandler dbusMessageHandler;
     private int subscriberCount;
 
@@ -100,6 +103,7 @@ public class DbusSignalImpl implements Signal {
         updateDevices();
         updateGroups();
         updateConfiguration();
+        updateIdentities();
     }
 
     public void close() {
@@ -114,6 +118,7 @@ public class DbusSignalImpl implements Signal {
         unExportDevices();
         unExportGroups();
         unExportConfiguration();
+        unExportIdentities();
         connection.unExportObject(this.objectPath);
     }
 
@@ -1029,6 +1034,109 @@ public class DbusSignalImpl implements Signal {
         }
     }
 
+    private void updateIdentities() {
+        List<org.asamk.signal.manager.api.Identity> identities;
+        identities = m.getIdentities();
+
+        unExportIdentities();
+
+        identities.forEach(i -> {
+            final var object = new DbusSignalIdentityImpl(i);
+            exportObject(object);
+            this.identities.add(new StructIdentity(new DBusPath(object.getObjectPath()),
+                emptyIfNull(i.recipient().getIdentifier()),
+                i.recipient().getLegacyIdentifier()));
+        });
+    }
+
+    private static String getIdentityObjectPath(String basePath, String id) {
+        return basePath + "/Identities/" + makeValidObjectPathElement(id);
+    }
+
+    private void unExportIdentities() {
+        this.identities.stream().map(StructIdentity::getObjectPath).map(DBusPath::getPath).forEach(connection::unExportObject);
+        this.identities.clear();
+    }
+
+    @Override
+    public DBusPath getIdentity(String number) throws Error.Failure {
+
+        final var found = identities.stream()
+                            .filter(identity -> identity.getName().equals(number))
+                            .findFirst();
+        
+        if (found.isEmpty()) {
+            throw new Error.Failure("Identity for " + number + " unkown");
+        }
+        return found.get().getObjectPath();
+    }
+
+    @Override
+    public List<StructIdentity> listIdentities() {
+        updateIdentities();
+        return this.identities;
+    }
+
+    public class DbusSignalIdentityImpl extends DbusProperties implements Signal.Identity {
+
+        private final org.asamk.signal.manager.api.Identity identity;
+
+        public DbusSignalIdentityImpl(final org.asamk.signal.manager.api.Identity identity) {
+            this.identity=identity;
+            super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Identity",
+                    List.of(new DbusProperty<>("Number", () -> identity.recipient().number().orElse("")),
+                            new DbusProperty<>("Uuid", () -> identity.recipient().uuid().map(UUID::toString).orElse("")),
+                            new DbusProperty<>("Fingerprint", () -> identity.getFingerprint()),
+                            new DbusProperty<>("SafetyNumber", identity::safetyNumber),
+                            new DbusProperty<>("ScannableSafetyNumber", identity::scannableSafetyNumber),
+                            new DbusProperty<>("TrustLevel", identity::trustLevel),
+                            new DbusProperty<>("AddedDate", identity::dateAddedTimestamp)
+                            )));
+        }
+
+        @Override
+        public String getObjectPath() {
+            return getIdentityObjectPath(objectPath, identity.recipient().getLegacyIdentifier());
+        }
+
+        @Override
+        public void trust() throws Error.Failure  {
+            var recipient=RecipientIdentifier.Single.fromAddress(identity.recipient());
+            try {
+                m.trustIdentityAllKeys(recipient);
+            } catch (UnregisteredRecipientException e) {
+                throw new Error.Failure("The user " + e.getSender().getIdentifier() + " is not registered.");
+            }
+        };
+
+        @Override
+        public void trustVerified(String safetyNumber) throws Error.Failure {
+             var recipient = RecipientIdentifier.Single.fromAddress(identity.recipient());
+            if (safetyNumber == null) {
+                throw new Error.Failure(
+                    "You need to specify a fingerprint/safety number");
+            }
+            final IdentityVerificationCode verificationCode;
+            try {
+                verificationCode = IdentityVerificationCode.parse(safetyNumber);
+            } catch (Exception e) {
+                throw new Error.Failure(
+                        "Safety number has invalid format, either specify the old hex fingerprint or the new safety number");
+            }
+
+            try {
+                final var res = m.trustIdentityVerified(recipient, verificationCode);
+                if (!res) {
+                    throw new Error.Failure(
+                            "Failed to set the trust for this number, make sure the number and the fingerprint/safety number are correct.");
+                }
+            } catch (UnregisteredRecipientException e) {
+                throw new Error.Failure("The user " + e.getSender().getIdentifier() + " is not registered.");
+            }
+        }
+    }
+
     public class DbusSignalDeviceImpl extends DbusProperties implements Signal.Device {
 
         private final org.asamk.signal.manager.api.Device device;