From 1b170c74c0725dc1bd8817f3ae8a33cc7bc3d130 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Wed, 24 Nov 2021 09:38:18 +0000 Subject: [PATCH] Modified storage code to support 2 new settings: publicDataEnabled - whether to store decryptable data (default true) privateDataEnabled - whether to store data without a decryption key (default false) --- .../qortal/arbitrary/ArbitraryDataReader.java | 1 + .../ArbitraryDataCleanupManager.java | 6 +-- .../arbitrary/ArbitraryDataManager.java | 6 +-- .../ArbitraryDataStorageManager.java | 46 +++++++++++++++---- .../java/org/qortal/settings/Settings.java | 13 ++++++ .../ArbitraryDataStoragePolicyTests.java | 2 + 6 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 src/test/java/org/qortal/test/arbitrary/ArbitraryDataStoragePolicyTests.java diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java index 54d014ac..cc701d85 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java @@ -372,6 +372,7 @@ public class ArbitraryDataReader { } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | IOException | InvalidKeyException e) { + // TODO: delete files and blacklist this resource if privateDataEnabled is false throw new DataException(String.format("Unable to decrypt file at path %s: %s", this.filePath, e.getMessage())); } } else { diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java index 1636703f..3d19bd96 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java @@ -128,9 +128,9 @@ public class ArbitraryDataCleanupManager extends Thread { // Check to see if we should be hosting data for this transaction at all - if (!storageManager.canStoreDataForName(arbitraryTransactionData.getName())) { - LOGGER.info("Deleting transaction {} because we can't host data for name {}", - Base58.encode(arbitraryTransactionData.getSignature()), arbitraryTransactionData.getName()); + if (!storageManager.canStoreData(arbitraryTransactionData)) { + LOGGER.info("Deleting transaction {} because we can't host its data", + Base58.encode(arbitraryTransactionData.getSignature())); ArbitraryTransactionUtils.deleteCompleteFileAndChunks(arbitraryTransactionData); continue; } diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java index 7daa5424..cf7058d8 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java @@ -190,7 +190,7 @@ public class ArbitraryDataManager extends Thread { ArbitraryTransactionData arbitraryTransactionData = (ArbitraryTransactionData) arbitraryTransaction.getTransactionData(); // Skip transactions that we don't need to proactively store data for - if (!storageManager.shouldPreFetchDataForName(arbitraryTransactionData.getName())) { + if (!storageManager.shouldPreFetchData(arbitraryTransactionData)) { iterator.remove(); continue; } @@ -680,7 +680,7 @@ public class ArbitraryDataManager extends Thread { // We may also need to broadcast to the network that we are now hosting files for this transaction, // but only if these files are in accordance with our storage policy - if (ArbitraryDataStorageManager.getInstance().canStoreDataForName(arbitraryTransactionData.getName())) { + if (ArbitraryDataStorageManager.getInstance().canStoreData(arbitraryTransactionData)) { // Use a null peer address to indicate our own Message newArbitrarySignatureMessage = new ArbitrarySignaturesMessage(null, Arrays.asList(signature)); Network.getInstance().broadcast(broadcastPeer -> newArbitrarySignatureMessage); @@ -863,7 +863,7 @@ public class ArbitraryDataManager extends Thread { if (transactionData instanceof ArbitraryTransactionData) { // Check if we're even allowed to serve data for this transaction - if (ArbitraryDataStorageManager.getInstance().canStoreDataForName(transactionData.getName())) { + if (ArbitraryDataStorageManager.getInstance().canStoreData(transactionData)) { byte[] hash = transactionData.getData(); byte[] chunkHashes = transactionData.getChunkHashes(); diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java index 4a3f6721..34eb94ba 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java @@ -1,5 +1,6 @@ package org.qortal.controller.arbitrary; +import org.qortal.data.transaction.ArbitraryTransactionData; import org.qortal.list.ResourceListManager; import org.qortal.settings.Settings; @@ -25,7 +26,14 @@ public class ArbitraryDataStorageManager { return instance; } - public boolean canStoreDataForName(String name) { + public boolean canStoreData(ArbitraryTransactionData arbitraryTransactionData) { + String name = arbitraryTransactionData.getName(); + + // Don't store data unless it's an allowed type (public/private) + if (!this.isDataTypeAllowed(arbitraryTransactionData)) { + return false; + } + // Check if our storage policy and blacklist allows us to host data for this name switch (Settings.getInstance().getStoragePolicy()) { case FOLLOWED_AND_VIEWED: @@ -45,18 +53,19 @@ public class ArbitraryDataStorageManager { } } - public boolean isNameInBlacklist(String name) { - return ResourceListManager.getInstance().listContains("blacklist", "names", name, false); - } - - public boolean shouldPreFetchDataForName(String name) { + public boolean shouldPreFetchData(ArbitraryTransactionData arbitraryTransactionData) { + String name = arbitraryTransactionData.getName(); if (name == null) { - return this.shouldPreFetchDataWithoutName(); + return this.shouldPreFetchDataWithoutName(arbitraryTransactionData); } // Never fetch data from blacklisted names, even if they are followed if (this.isNameInBlacklist(name)) { return false; } + // Don't store data unless it's an allowed type (public/private) + if (!this.isDataTypeAllowed(arbitraryTransactionData)) { + return false; + } switch (Settings.getInstance().getStoragePolicy()) { case FOLLOWED: @@ -73,10 +82,10 @@ public class ArbitraryDataStorageManager { } } - private boolean shouldPreFetchDataWithoutName() { + private boolean shouldPreFetchDataWithoutName(ArbitraryTransactionData arbitraryTransactionData) { switch (Settings.getInstance().getStoragePolicy()) { case ALL: - return true; + return this.isDataTypeAllowed(arbitraryTransactionData); case NONE: case VIEWED: @@ -87,6 +96,25 @@ public class ArbitraryDataStorageManager { } } + private boolean isDataTypeAllowed(ArbitraryTransactionData arbitraryTransactionData) { + byte[] secret = arbitraryTransactionData.getSecret(); + boolean hasSecret = (secret != null && secret.length == 32); + + if (!Settings.getInstance().isPrivateDataEnabled() && !hasSecret) { + // Private data isn't enabled so we can't store data without a valid secret + return false; + } + if (!Settings.getInstance().isPublicDataEnabled() && hasSecret) { + // Public data isn't enabled so we can't store data with a secret + return false; + } + return true; + } + + public boolean isNameInBlacklist(String name) { + return ResourceListManager.getInstance().listContains("blacklist", "names", name, false); + } + private boolean isFollowingName(String name) { return ResourceListManager.getInstance().listContains("followed", "names", name, false); } diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index 8977d7b5..81dbb624 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -284,6 +284,11 @@ public class Settings { /** Whether to validate every layer when building arbitrary data, or just the final layer */ private boolean validateAllDataLayers = false; + /** Whether to allow public (decryptable) data to be stored */ + private boolean publicDataEnabled = true; + /** Whether to allow private (non-decryptable) data to be stored */ + private boolean privateDataEnabled = false; + // Domain mapping public static class DomainMap { @@ -827,4 +832,12 @@ public class Settings { public boolean shouldValidateAllDataLayers() { return this.validateAllDataLayers; } + + public boolean isPublicDataEnabled() { + return this.publicDataEnabled; + } + + public boolean isPrivateDataEnabled() { + return this.privateDataEnabled; + } } diff --git a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataStoragePolicyTests.java b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataStoragePolicyTests.java new file mode 100644 index 00000000..b0eed7ab --- /dev/null +++ b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataStoragePolicyTests.java @@ -0,0 +1,2 @@ +package org.qortal.test.arbitrary;public class ArbitraryDataStoragePolicyTests { +}