From eb98a9cacf71f18fce673a635eaf0c9d0004b024 Mon Sep 17 00:00:00 2001
From: Adrien Beraud <adrien.beraud@savoirfairelinux.com>
Date: Wed, 1 Mar 2017 22:40:45 +0100
Subject: [PATCH] device revocation: add DeviceRevocationEnded signal

* add DeviceRevocationEnded signal to allow client to
  be informed when revocation ends
* implement the signal in ringaccount
* remove device from account device list on revocation,
  send appropriate signal and sync devices

Tuleap: #1457
Change-Id: I0d804e77d420ffcfb01888e68aa07d58eba913e3
---
 src/client/ring_signal.cpp                 |  1 +
 src/dring/configurationmanager_interface.h |  4 ++
 src/ringdht/ringaccount.cpp                | 65 ++++++++++++----------
 src/ringdht/ringaccount.h                  |  5 +-
 4 files changed, 44 insertions(+), 31 deletions(-)

diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp
index 3a94ff3815..9c5c8667df 100644
--- a/src/client/ring_signal.cpp
+++ b/src/client/ring_signal.cpp
@@ -71,6 +71,7 @@ getSignalHandlers()
         exported_callback<DRing::ConfigurationSignal::RegisteredNameFound>(),
         exported_callback<DRing::ConfigurationSignal::MediaParametersChanged>(),
         exported_callback<DRing::ConfigurationSignal::MigrationEnded>(),
+        exported_callback<DRing::ConfigurationSignal::DeviceRevocationEnded>(),
         exported_callback<DRing::ConfigurationSignal::Error>(),
 #ifdef __ANDROID__
         exported_callback<DRing::ConfigurationSignal::GetHardwareAudioFormat>(),
diff --git a/src/dring/configurationmanager_interface.h b/src/dring/configurationmanager_interface.h
index 4d8b2c9d7b..7e4b942fe5 100644
--- a/src/dring/configurationmanager_interface.h
+++ b/src/dring/configurationmanager_interface.h
@@ -270,6 +270,10 @@ struct ConfigurationSignal {
                 constexpr static const char* name = "MigrationEnded";
                 using cb_type = void(const std::string& /*accountId*/, const std::string& /*state*/);
         };
+        struct DeviceRevocationEnded {
+                constexpr static const char* name = "DeviceRevocationEnded";
+                using cb_type = void(const std::string& /*accountId*/, const std::string& /*device*/, int /*status*/);
+        };
         /**
          * These are special getters for Android and UWP, so the daemon can retreive
          * information only accessible through their respective platform APIs
diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index 59c00abd53..197045ca99 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -79,7 +79,6 @@
 namespace ring {
 
 using sip_utils::CONST_PJ_STR;
-using std::chrono::system_clock;
 
 namespace Migration {
 
@@ -151,13 +150,13 @@ RingAccount::SavedTrustRequest {
 struct
 RingAccount::TrustRequest {
     dht::InfoHash from_device;
-    std::chrono::system_clock::time_point received;
+    time_point received;
     std::vector<uint8_t> payload;
     TrustRequest() {}
-    TrustRequest(dht::InfoHash device, std::chrono::system_clock::time_point r, std::vector<uint8_t>&& payload)
+    TrustRequest(dht::InfoHash device, time_point r, std::vector<uint8_t>&& payload)
             : from_device(device), received(r), payload(std::move(payload)) {}
     TrustRequest(SavedTrustRequest&& sr)
-        : from_device(sr.device), received(system_clock::from_time_t(sr.received)), payload(std::move(sr.payload)) {}
+        : from_device(sr.device), received(clock::from_time_t(sr.received)), payload(std::move(sr.payload)) {}
 };
 
 struct RingAccount::Contact
@@ -197,9 +196,6 @@ struct RingAccount::Contact
  */
 struct RingAccount::KnownDevice
 {
-    using clock = std::chrono::system_clock;
-    using time_point = clock::time_point;
-
     /** Device certificate */
     std::shared_ptr<dht::crypto::Certificate> certificate;
 
@@ -1084,7 +1080,7 @@ std::pair<std::vector<uint8_t>, dht::InfoHash>
 RingAccount::computeKeys(const std::string& password, const std::string& pin, bool previous)
 {
     // Compute time seed
-    auto now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
+    auto now = std::chrono::duration_cast<std::chrono::seconds>(clock::now().time_since_epoch());
     auto tseed = now.count() / std::chrono::duration_cast<std::chrono::seconds>(EXPORT_KEY_RENEWAL_TIME).count();
     if (previous)
         tseed--;
@@ -1163,13 +1159,19 @@ RingAccount::revokeDevice(const std::string& password, const std::string& device
         [this, password] { return readArchive(password); });
     auto sthis = shared();
     findCertificate(dht::InfoHash(device),
-                    [fa,sthis,password](const std::shared_ptr<dht::crypto::Certificate>& crt) mutable
+                    [fa,sthis,password,device](const std::shared_ptr<dht::crypto::Certificate>& crt) mutable
     {
-        sthis->foundAccountDevice(crt);
+        auto& this_ = *sthis;
+        if (not crt) {
+            emitSignal<DRing::ConfigurationSignal::DeviceRevocationEnded>(this_.getAccountID(), device, 2);
+            return;
+        }
+        this_.foundAccountDevice(crt);
         ArchiveContent a;
         try {
             a = fa->get();
         } catch (...) {
+            emitSignal<DRing::ConfigurationSignal::DeviceRevocationEnded>(this_.getAccountID(), device, 1);
             return;
         }
         // Add revoked device to the revocation list and resign it
@@ -1179,8 +1181,13 @@ RingAccount::revokeDevice(const std::string& password, const std::string& device
         a.revoked->sign(a.id);
         // add to CRL cache
         tls::CertificateStore::instance().pinRevocationList(a.id.second->getId().toString(), a.revoked);
-        tls::CertificateStore::instance().loadRevocations(*sthis->identity_.second->issuer);
-        sthis->saveArchive(a, password);
+        tls::CertificateStore::instance().loadRevocations(*this_.identity_.second->issuer);
+        this_.saveArchive(a, password);
+        this_.knownDevices_.erase(crt->getId());
+        this_.saveKnownDevices();
+        emitSignal<DRing::ConfigurationSignal::DeviceRevocationEnded>(this_.getAccountID(), device, 0);
+        emitSignal<DRing::ConfigurationSignal::KnownDevicesChanged>(this_.getAccountID(), this_.getKnownDevices());
+        this_.syncDevices();
     });
     return true;
 }
@@ -1359,7 +1366,7 @@ RingAccount::needsMigration(const dht::crypto::Identity& id)
             RING_WARN("certificate %s is not a CA, needs update.", cert->getId().toString().c_str());
             return true;
         }
-        if (cert->getExpiration() < std::chrono::system_clock::now()) {
+        if (cert->getExpiration() < clock::now()) {
             RING_WARN("certificate %s is expired, needs update.", cert->getId().toString().c_str());
             return true;
         }
@@ -1388,14 +1395,14 @@ RingAccount::updateCertificates(ArchiveContent& archive, dht::crypto::Identity&
     auto& cert = archive.id.second;
     auto ca = cert->issuer;
     if (ca and not ca->issuer) {
-        if (not ca->isCA() or ca->getExpiration() < std::chrono::system_clock::now()) {
+        if (not ca->isCA() or ca->getExpiration() < clock::now()) {
             ca = std::make_shared<Certificate>(Certificate::generate(*archive.ca_key, "Ring CA", {}, true));
             updated = true;
         }
     }
 
     // Update certificate
-    if (updated or not cert->isCA() or cert->getExpiration() < std::chrono::system_clock::now()) {
+    if (updated or not cert->isCA() or cert->getExpiration() < clock::now()) {
         cert = std::make_shared<Certificate>(Certificate::generate(*archive.id.first, "Ring", dht::crypto::Identity{archive.ca_key, ca}, true));
         updated = true;
     }
@@ -2172,11 +2179,11 @@ RingAccount::doRegister_()
                         auto req = this_.trustRequests_.find(peer_account);
                         if (req == this_.trustRequests_.end()) {
                             req = this_.trustRequests_.emplace(peer_account, TrustRequest{
-                                v.from, std::chrono::system_clock::now(), std::move(v.payload)
+                                v.from, clock::now(), std::move(v.payload)
                             }).first;
                         } else {
                             req->second.from_device = v.from;
-                            req->second.received = std::chrono::system_clock::now();
+                            req->second.received = clock::now();
                             req->second.payload = std::move(v.payload);
                         }
                         this_.saveTrustRequests();
@@ -2184,7 +2191,7 @@ RingAccount::doRegister_()
                             this_.getAccountID(),
                             req->first.toString(),
                             req->second.payload,
-                            std::chrono::system_clock::to_time_t(req->second.received)
+                            clock::to_time_t(req->second.received)
                         );
                     }
                 });
@@ -2224,7 +2231,7 @@ RingAccount::doRegister_()
                 this_.onPeerMessage(v.from, [shared, v, inboxDeviceKey](const std::shared_ptr<dht::crypto::Certificate>&,
                                                                         const dht::InfoHash& peer_account)
                 {
-                    auto now = system_clock::to_time_t(system_clock::now());
+                    auto now = clock::to_time_t(clock::now());
                     std::map<std::string, std::string> payloads = {{"text/plain",
                                                                     utf8_make_valid(v.msg)}};
                     shared->onTextMessage(peer_account.toString(), payloads);
@@ -2319,7 +2326,7 @@ RingAccount::incomingCall(dht::IceCandidates&& msg, const std::shared_ptr<dht::c
 }
 
 bool
-RingAccount::foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, const std::string& name)
+RingAccount::foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, const std::string& name, const time_point& updated)
 {
     if (not crt)
         return false;
@@ -2335,7 +2342,7 @@ RingAccount::foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>&
     }
 
     // insert device
-    auto it = knownDevices_.emplace(crt->getId(), KnownDevice{crt, name});
+    auto it = knownDevices_.emplace(crt->getId(), KnownDevice{crt, name, updated});
     if (it.second) {
         RING_WARN("[Account %s] Found known account device: %s", getAccountID().c_str(), crt->getId().toString().c_str());
         tls::CertificateStore::instance().pinCertificate(crt);
@@ -2637,7 +2644,6 @@ RingAccount::loadKnownDevices()
         RING_DBG("[Account %s] loading known account device %s %s", getAccountID().c_str(),
                                                                     d.second.first.c_str(),
                                                                     d.first.toString().c_str());
-        using clock = std::chrono::system_clock;
         if (auto crt = tls::CertificateStore::instance().getCertificate(d.first.toString()))
             if (crt->issuer and crt->issuer->getId() == identity_.second->issuer->getId())
                 knownDevices_.emplace(d.first,
@@ -2660,7 +2666,7 @@ RingAccount::saveKnownDevices() const
 
     std::map<dht::InfoHash, std::pair<std::string, uint64_t>> devices;
     for (const auto& id : knownDevices_)
-        devices.emplace(id.first, std::make_pair(id.second.name, system_clock::to_time_t(id.second.last_sync)));
+        devices.emplace(id.first, std::make_pair(id.second.name, clock::to_time_t(id.second.last_sync)));
 
     msgpack::pack(file, devices);
 }
@@ -2756,7 +2762,7 @@ RingAccount::loadDhParams(const std::string path)
 {
     try {
         // writeTime throw exception if file doesn't exist
-        auto duration = system_clock::now() - fileutils::writeTime(path);
+        auto duration = clock::now() - fileutils::writeTime(path);
         if (duration >= std::chrono::hours(24 * 3)) // file is valid only 3 days
             throw std::runtime_error("file too old");
 
@@ -2942,7 +2948,7 @@ RingAccount::getTrustRequests() const
 {
     std::map<std::string, std::string> ret;
     for (const auto& r : trustRequests_)
-        ret.emplace(r.first.toString(), ring::to_string(std::chrono::system_clock::to_time_t(r.second.received)));
+        ret.emplace(r.first.toString(), ring::to_string(clock::to_time_t(r.second.received)));
     return ret;
 }
 
@@ -3011,7 +3017,7 @@ RingAccount::saveTrustRequests() const
 
     std::map<dht::InfoHash, SavedTrustRequest> requests;
     for (const auto& req : trustRequests_)
-        requests.emplace(req.first, SavedTrustRequest{req.second.from_device, system_clock::to_time_t(req.second.received), req.second.payload});
+        requests.emplace(req.first, SavedTrustRequest{req.second.from_device, clock::to_time_t(req.second.received), req.second.payload});
 
     msgpack::pack(file, requests);
 }
@@ -3038,7 +3044,7 @@ RingAccount::loadTrustRequests()
             getAccountID(),
             r.first.toString(),
             r.second.payload,
-            std::chrono::system_clock::to_time_t(r.second.received)
+            clock::to_time_t(r.second.received)
         );
     }
 }
@@ -3050,7 +3056,7 @@ RingAccount::syncDevices()
 {
     RING_DBG("[Account %s] building device sync from %s %s", getAccountID().c_str(), ringDeviceName_.c_str(), ringDeviceId_.c_str());
     DeviceSync sync_data;
-    sync_data.date = std::chrono::system_clock::now().time_since_epoch().count();
+    sync_data.date = clock::now().time_since_epoch().count();
     sync_data.device_name = ringDeviceName_;
     sync_data.peers = contacts_;
     for (const auto& dev : knownDevices_) {
@@ -3077,7 +3083,6 @@ RingAccount::onReceiveDeviceSync(DeviceSync&& sync)
         RING_WARN("[Account %s] dropping sync data from unknown device", getAccountID().c_str());
         return;
     }
-    using clock = std::chrono::system_clock;
     auto sync_date = clock::time_point(clock::duration(sync.date));
     if (it->second.last_sync >= sync_date) {
         RING_DBG("[Account %s] dropping outdated sync data", getAccountID().c_str());
@@ -3179,7 +3184,7 @@ RingAccount::sendTextMessage(const std::string& to, const std::map<std::string,
 
     auto toUri = parseRingUri(to);
     auto toH = dht::InfoHash(toUri);
-    auto now = system_clock::to_time_t(system_clock::now());
+    auto now = clock::to_time_t(clock::now());
 
     struct PendingConfirmation {
         bool replied {false};
diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h
index 3024c7fa6f..77ee97fdcc 100644
--- a/src/ringdht/ringaccount.h
+++ b/src/ringdht/ringaccount.h
@@ -323,6 +323,9 @@ class RingAccount : public SIPAccountBase {
     private:
         NON_COPYABLE(RingAccount);
 
+        using clock = std::chrono::system_clock;
+        using time_point = clock::time_point;
+
         /**
          * Private structures
          */
@@ -396,7 +399,7 @@ class RingAccount : public SIPAccountBase {
          * Inform that a potential account device have been found.
          * Returns true if the device have been validated to be part of this account
          */
-        bool foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, const std::string& name = {});
+        bool foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, const std::string& name = {}, const time_point& last_sync = time_point::min());
 
         /**
          * Inform that a potential peer device have been found.
-- 
GitLab