From 4347d54a4b15c9311fa839f48fcad4e3ac297981 Mon Sep 17 00:00:00 2001
From: blavenie <benoit.lavenier@e-is.pro>
Date: Wed, 18 Oct 2017 16:45:06 +0200
Subject: [PATCH] [enh] Allow node admin to remove some data

---
 .../src/test/es-home/config/elasticsearch.yml |  1 +
 .../duniter/elasticsearch/PluginSettings.java |  8 ++++
 .../elasticsearch/synchro/SynchroService.java | 12 +++++-
 .../elasticsearch/user/PluginSettings.java    |  5 +++
 .../user/service/HistoryService.java          | 38 ++++++++++++++-----
 5 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/duniter4j-es-assembly/src/test/es-home/config/elasticsearch.yml b/duniter4j-es-assembly/src/test/es-home/config/elasticsearch.yml
index f21eee21..589d5d48 100644
--- a/duniter4j-es-assembly/src/test/es-home/config/elasticsearch.yml
+++ b/duniter4j-es-assembly/src/test/es-home/config/elasticsearch.yml
@@ -188,6 +188,7 @@ duniter.p2p.discovery.enable: false
 duniter.p2p.includes.pubkeys: [
   "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE"
 ]
+#duniter.p2p.fullResyncAtStartup: true
 #
 # ---------------------------------- Duniter4j Mail module -----------------------
 #
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
index caf1240d..62e193b4 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
@@ -226,6 +226,10 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
         return settings.getAsBoolean("duniter.p2p.ws.enable", true);
     }
 
+    public boolean fullResyncAtStartup()  {
+        return settings.getAsBoolean("duniter.p2p.fullResyncAtStartup", false);
+    }
+
     public int getSynchroTimeOffset()  {
         return settings.getAsInt("duniter.p2p.peerTimeOffset", 60*60/*=1hour*/);
     }
@@ -298,6 +302,10 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
         return settings.getAsInt("duniter.document.time.maxFutureDelta", 600); // in seconds = 10min
     }
 
+    public boolean allowDocumentDeletionByAdmin() {
+        return settings.getAsBoolean("duniter.document.allowAdminDeletion", true); //
+    }
+
     public String getWebSocketHost()  {
         return settings.get("network.host", "locahost");
     }
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/synchro/SynchroService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/synchro/SynchroService.java
index 2639e01d..5ecb5611 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/synchro/SynchroService.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/synchro/SynchroService.java
@@ -75,6 +75,7 @@ public class SynchroService extends AbstractService {
     private final SynchroExecutionDao synchroExecutionDao;
     private List<WebsocketClientEndpoint> wsClientEndpoints = Lists.newArrayList();
     private List<SynchroAction> actions = Lists.newArrayList();
+    private boolean forceFullResync = false;
 
     @Inject
     public SynchroService(Duniter4jClient client,
@@ -124,7 +125,12 @@ public class SynchroService extends AbstractService {
 
             // If can be launched now: do it
             if (launchAtStartup) {
+
+                forceFullResync = pluginSettings.fullResyncAtStartup();
+
                 synchronize();
+
+                forceFullResync = false;
             }
 
             // Schedule next execution, to 5 min before each hour
@@ -185,7 +191,8 @@ public class SynchroService extends AbstractService {
             } else {
                 logger.info(String.format("[%s] [%s] Synchronization [OK] - no endpoint to synchronize", currencyId, peerApiFilter.name()));
             }
-            }));
+            }
+        ));
     }
 
     public SynchroResult synchronizePeer(final Peer peer, boolean enableSynchroWebsocket) {
@@ -203,7 +210,7 @@ public class SynchroService extends AbstractService {
         // Get the last execution time (or 0 is never synchronized)
         // If not the first synchro, add a delay to last execution time
         // to avoid missing data because incorrect clock configuration
-        long lastExecutionTime = getLastExecutionTime(peer);
+        long lastExecutionTime = forceFullResync ? 0 : getLastExecutionTime(peer);
         if (logger.isDebugEnabled() && lastExecutionTime > 0) {
             logger.debug(String.format("[%s] [%s] Found last synchronization execution at {%s}. Will apply time offset of {-%s ms}", peer.getCurrency(), peer,
                     DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
@@ -213,6 +220,7 @@ public class SynchroService extends AbstractService {
 
         final long fromTime = lastExecutionTime > 0 ? lastExecutionTime - pluginSettings.getSynchroTimeOffset() : 0;
 
+
         if (logger.isInfoEnabled()) {
             if (fromTime == 0) {
                 logger.info(String.format("[%s] [%s] Synchronization {ALL}...", peer.getCurrency(), peer));
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java
index 3e880d35..22370ad4 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java
@@ -185,6 +185,9 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
         return delegate.getKeyringSecretKey();
     }
 
+    public boolean allowDocumentDeletionByAdmin() {
+        return delegate.allowDocumentDeletionByAdmin();
+    }
 
     public void addI18nBundleName(String bundleName) {
         delegate.addI18nBundleName(bundleName);
@@ -210,6 +213,8 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
     }
 
 
+
+
     /* -- protected methods -- */
 
     protected String getI18nBundleName() {
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java
index 5d644792..15bff0ac 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/HistoryService.java
@@ -29,6 +29,7 @@ import org.duniter.core.client.model.elasticsearch.DeleteRecord;
 import org.duniter.core.exception.TechnicalException;
 import org.duniter.core.service.CryptoService;
 import org.duniter.elasticsearch.client.Duniter4jClient;
+import org.duniter.elasticsearch.exception.AccessDeniedException;
 import org.duniter.elasticsearch.exception.NotFoundException;
 import org.duniter.elasticsearch.user.PluginSettings;
 import org.duniter.elasticsearch.user.model.Message;
@@ -46,6 +47,7 @@ import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 
 import java.io.IOException;
+import java.util.Objects;
 
 /**
  * Created by Benoit on 30/03/2015.
@@ -147,17 +149,33 @@ public class HistoryService extends AbstractService {
             throw new NotFoundException(String.format("Index [%s] not exists.", index));
         }
 
-        // Message: check if deletion issuer is the message recipient
-        if (MessageService.INDEX.equals(index) && MessageService.INBOX_TYPE.equals(type)) {
-            client.checkSameDocumentField(index, type, id, Message.PROPERTY_RECIPIENT, issuer);
-        }
-        // Invitation: check if deletion issuer is the invitation recipient
-        else if (UserInvitationService.INDEX.equals(index)) {
-            client.checkSameDocumentField(index, type, id, Message.PROPERTY_RECIPIENT, issuer);
+        try {
+            // Message: check if deletion issuer is the message recipient
+            if (MessageService.INDEX.equals(index) && MessageService.INBOX_TYPE.equals(type)) {
+                client.checkSameDocumentField(index, type, id, Message.PROPERTY_RECIPIENT, issuer);
+            }
+            // Invitation: check if deletion issuer is the invitation recipient
+            else if (UserInvitationService.INDEX.equals(index)) {
+
+                    client.checkSameDocumentField(index, type, id, Message.PROPERTY_RECIPIENT, issuer);
+
+            }
+            else {
+                // Check same document issuer
+                client.checkSameDocumentIssuer(index, type, id, issuer);
+            }
         }
-        else {
-            // Check same document issuer
-            client.checkSameDocumentIssuer(index, type, id, issuer);
+        catch(AccessDeniedException e) {
+            // Check if admin ask the deletion
+            // If deletion done by admin: continue if allow in settings
+            if (!pluginSettings.isRandomNodeKeypair()
+                    && pluginSettings.allowDocumentDeletionByAdmin()
+                    && Objects.equals(issuer, pluginSettings.getNodePubkey())) {
+                logger.warn(String.format("[%s/%s] Deletion forced by admin, on doc [%s]", index, type, id));
+            }
+            else {
+                throw e;
+            }
         }
 
         // Check time is valid - fix #27
-- 
GitLab