diff --git a/src/main/java/org/qortal/api/model/AddressListRequest.java b/src/main/java/org/qortal/api/model/ListRequest.java similarity index 63% rename from src/main/java/org/qortal/api/model/AddressListRequest.java rename to src/main/java/org/qortal/api/model/ListRequest.java index c600609f..c5ebb48a 100644 --- a/src/main/java/org/qortal/api/model/AddressListRequest.java +++ b/src/main/java/org/qortal/api/model/ListRequest.java @@ -7,12 +7,12 @@ import javax.xml.bind.annotation.XmlAccessorType; import java.util.List; @XmlAccessorType(XmlAccessType.FIELD) -public class AddressListRequest { +public class ListRequest { - @Schema(description = "A list of addresses") - public List addresses; + @Schema(description = "A list of items") + public List items; - public AddressListRequest() { + public ListRequest() { } } diff --git a/src/main/java/org/qortal/api/resource/ListsResource.java b/src/main/java/org/qortal/api/resource/ListsResource.java index dea6690c..66088ba0 100644 --- a/src/main/java/org/qortal/api/resource/ListsResource.java +++ b/src/main/java/org/qortal/api/resource/ListsResource.java @@ -9,7 +9,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import org.qortal.api.*; -import org.qortal.api.model.AddressListRequest; +import org.qortal.api.model.ListRequest; import org.qortal.crypto.Crypto; import org.qortal.data.account.AccountData; import org.qortal.list.ResourceListManager; @@ -29,39 +29,9 @@ public class ListsResource { @Context HttpServletRequest request; - - @POST - @Path("/blacklist/address/{address}") - @Operation( - summary = "Add a QORT address to the local blacklist", - responses = { - @ApiResponse( - description = "Returns true on success, or an exception on failure", - content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "boolean")) - ) - } - ) - @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE}) - public String addAddressToBlacklist(@PathParam("address") String address) { - Security.checkApiCallAllowed(request); - - if (!Crypto.isValidAddress(address)) - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS); - try (final Repository repository = RepositoryManager.getRepository()) { - AccountData accountData = repository.getAccountRepository().getAccount(address); - // Not found? - if (accountData == null) - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.ADDRESS_UNKNOWN); - // Valid address, so go ahead and blacklist it - boolean success = ResourceListManager.getInstance().addAddressToBlacklist(address, true); - - return success ? "true" : "false"; - } catch (DataException e) { - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); - } - } + /* Address blacklist */ @POST @Path("/blacklist/addresses") @@ -72,7 +42,7 @@ public class ListsResource { content = @Content( mediaType = MediaType.APPLICATION_JSON, schema = @Schema( - implementation = AddressListRequest.class + implementation = ListRequest.class ) ) ), @@ -86,10 +56,10 @@ public class ListsResource { } ) @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE}) - public String addAddressesToBlacklist(AddressListRequest addressListRequest) { + public String addAddressesToBlacklist(ListRequest listRequest) { Security.checkApiCallAllowed(request); - if (addressListRequest == null || addressListRequest.addresses == null) { + if (listRequest == null || listRequest.items == null) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA); } @@ -98,7 +68,7 @@ public class ListsResource { try (final Repository repository = RepositoryManager.getRepository()) { - for (String address : addressListRequest.addresses) { + for (String address : listRequest.items) { if (!Crypto.isValidAddress(address)) { errorCount++; @@ -113,7 +83,7 @@ public class ListsResource { } // Valid address, so go ahead and blacklist it - boolean success = ResourceListManager.getInstance().addAddressToBlacklist(address, false); + boolean success = ResourceListManager.getInstance().addToList("blacklist", "addresses", address, false); if (success) { successCount++; } @@ -127,50 +97,16 @@ public class ListsResource { if (successCount > 0 && errorCount == 0) { // All were successful, so save the blacklist - ResourceListManager.getInstance().saveBlacklist(); + ResourceListManager.getInstance().saveList("blacklist", "addresses"); return "true"; } else { // Something went wrong, so revert - ResourceListManager.getInstance().revertBlacklist(); + ResourceListManager.getInstance().revertList("blacklist", "addresses"); return "false"; } } - - @DELETE - @Path("/blacklist/address/{address}") - @Operation( - summary = "Remove a QORT address from the local blacklist", - responses = { - @ApiResponse( - description = "Returns true on success, or an exception on failure", - content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "boolean")) - ) - } - ) - @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE}) - public String removeAddressFromBlacklist(@PathParam("address") String address) { - Security.checkApiCallAllowed(request); - - if (!Crypto.isValidAddress(address)) - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS); - - try (final Repository repository = RepositoryManager.getRepository()) { - AccountData accountData = repository.getAccountRepository().getAccount(address); - // Not found? - if (accountData == null) - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.ADDRESS_UNKNOWN); - - // Valid address, so go ahead and blacklist it - boolean success = ResourceListManager.getInstance().removeAddressFromBlacklist(address, true); - - return success ? "true" : "false"; - } catch (DataException e) { - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); - } - } - @DELETE @Path("/blacklist/addresses") @Operation( @@ -180,7 +116,7 @@ public class ListsResource { content = @Content( mediaType = MediaType.APPLICATION_JSON, schema = @Schema( - implementation = AddressListRequest.class + implementation = ListRequest.class ) ) ), @@ -194,10 +130,10 @@ public class ListsResource { } ) @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE}) - public String removeAddressesFromBlacklist(AddressListRequest addressListRequest) { + public String removeAddressesFromBlacklist(ListRequest listRequest) { Security.checkApiCallAllowed(request); - if (addressListRequest == null || addressListRequest.addresses == null) { + if (listRequest == null || listRequest.items == null) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA); } @@ -206,7 +142,7 @@ public class ListsResource { try (final Repository repository = RepositoryManager.getRepository()) { - for (String address : addressListRequest.addresses) { + for (String address : listRequest.items) { if (!Crypto.isValidAddress(address)) { errorCount++; @@ -222,7 +158,7 @@ public class ListsResource { // Valid address, so go ahead and blacklist it // Don't save as we will do this at the end of the process - boolean success = ResourceListManager.getInstance().removeAddressFromBlacklist(address, false); + boolean success = ResourceListManager.getInstance().removeFromList("blacklist", "addresses", address, false); if (success) { successCount++; } @@ -236,12 +172,12 @@ public class ListsResource { if (successCount > 0 && errorCount == 0) { // All were successful, so save the blacklist - ResourceListManager.getInstance().saveBlacklist(); + ResourceListManager.getInstance().saveList("blacklist", "addresses"); return "true"; } else { // Something went wrong, so revert - ResourceListManager.getInstance().revertBlacklist(); + ResourceListManager.getInstance().revertList("blacklist", "addresses"); return "false"; } } @@ -259,40 +195,7 @@ public class ListsResource { ) public String getAddressBlacklist() { Security.checkApiCallAllowed(request); - return ResourceListManager.getInstance().getBlacklistJSONString(); - } - - @GET - @Path("/blacklist/address/{address}") - @Operation( - summary = "Check if an address is present in the local blacklist", - responses = { - @ApiResponse( - description = "Returns true or false if the list was queried, or an exception on failure", - content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "boolean")) - ) - } - ) - @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE}) - public String checkAddressInBlacklist(@PathParam("address") String address) { - Security.checkApiCallAllowed(request); - - if (!Crypto.isValidAddress(address)) - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS); - - try (final Repository repository = RepositoryManager.getRepository()) { - AccountData accountData = repository.getAccountRepository().getAccount(address); - // Not found? - if (accountData == null) - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.ADDRESS_UNKNOWN); - - // Valid address, so go ahead and blacklist it - boolean blacklisted = ResourceListManager.getInstance().isAddressInBlacklist(address); - - return blacklisted ? "true" : "false"; - } catch (DataException e) { - throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); - } + return ResourceListManager.getInstance().getJSONStringForList("blacklist", "addresses"); } } diff --git a/src/main/java/org/qortal/list/ResourceList.java b/src/main/java/org/qortal/list/ResourceList.java index c80deac3..e682441c 100644 --- a/src/main/java/org/qortal/list/ResourceList.java +++ b/src/main/java/org/qortal/list/ResourceList.java @@ -21,7 +21,7 @@ public class ResourceList { private String category; private String resourceName; - private List list; + private List list = new ArrayList<>(); /** * ResourceList @@ -36,7 +36,6 @@ public class ResourceList { public ResourceList(String category, String resourceName) throws IOException { this.category = category; this.resourceName = resourceName; - this.list = new ArrayList<>(); this.load(); } @@ -45,7 +44,7 @@ public class ResourceList { private Path getFilePath() { String pathString = String.format("%s%s%s_%s.json", Settings.getInstance().getListsPath(), - File.separator, this.resourceName, this.category); + File.separator, this.category, this.resourceName); return Paths.get(pathString); } @@ -154,4 +153,16 @@ public class ResourceList { return ResourceList.listToJSONString(this.list); } + public String getCategory() { + return this.category; + } + + public String getResourceName() { + return this.resourceName; + } + + public String toString() { + return String.format("%s %s", this.category, this.resourceName); + } + } diff --git a/src/main/java/org/qortal/list/ResourceListManager.java b/src/main/java/org/qortal/list/ResourceListManager.java index c4b94347..4d0d19c3 100644 --- a/src/main/java/org/qortal/list/ResourceListManager.java +++ b/src/main/java/org/qortal/list/ResourceListManager.java @@ -4,92 +4,125 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class ResourceListManager { private static final Logger LOGGER = LogManager.getLogger(ResourceListManager.class); private static ResourceListManager instance; - private ResourceList addressBlacklist; + private List lists = new ArrayList<>(); + public ResourceListManager() { - try { - this.addressBlacklist = new ResourceList("blacklist", "address"); - } catch (IOException e) { - LOGGER.info("Error while loading address blacklist. Blocking is currently unavailable."); - } } public static synchronized ResourceListManager getInstance() { if (instance == null) { instance = new ResourceListManager(); } - return instance; } - public boolean addAddressToBlacklist(String address, boolean save) { + private ResourceList getList(String category, String resourceName) { + for (ResourceList list : this.lists) { + if (Objects.equals(list.getCategory(), category) && + Objects.equals(list.getResourceName(), resourceName)) { + return list; + } + } + + // List doesn't exist in array yet, so create it + // This will load any existing data from the filesystem + try { + ResourceList list = new ResourceList(category, resourceName); + this.lists.add(list); + return list; + + } catch (IOException e) { + LOGGER.info("Unable to load or create list {} {}: {}", category, resourceName, e.getMessage()); + return null; + } + + } + + public boolean addToList(String category, String resourceName, String item, boolean save) { + ResourceList list = this.getList(category, resourceName); + if (list == null) { + return false; + } + try { - this.addressBlacklist.add(address); + list.add(item); if (save) { - this.addressBlacklist.save(); + list.save(); } return true; } catch (IllegalStateException | IOException e) { - LOGGER.info("Unable to add address to blacklist", e); + LOGGER.info(String.format("Unable to add item %s to list %s", item, list), e); return false; } } - public boolean removeAddressFromBlacklist(String address, boolean save) { + public boolean removeFromList(String category, String resourceName, String item, boolean save) { + ResourceList list = this.getList(category, resourceName); + if (list == null) { + return false; + } + try { - this.addressBlacklist.remove(address); + list.remove(item); if (save) { - this.addressBlacklist.save(); + list.save(); } return true; } catch (IllegalStateException | IOException e) { - LOGGER.info("Unable to remove address from blacklist", e); + LOGGER.info(String.format("Unable to remove item %s from list %s", item, list), e); return false; } } - public boolean isAddressInBlacklist(String address) { - if (this.addressBlacklist == null) { + public boolean listContains(String category, String resourceName, String address) { + ResourceList list = this.getList(category, resourceName); + if (list == null) { return false; } - return this.addressBlacklist.contains(address); + return list.contains(address); } - public void saveBlacklist() { - if (this.addressBlacklist == null) { + public void saveList(String category, String resourceName) { + ResourceList list = this.getList(category, resourceName); + if (list == null) { return; } try { - this.addressBlacklist.save(); + list.save(); } catch (IOException e) { - LOGGER.info("Unable to save blacklist - reverting back to last saved state"); - this.addressBlacklist.revert(); + LOGGER.info("Unable to save list {} - reverting back to last saved state", list); + list.revert(); } } - public void revertBlacklist() { - if (this.addressBlacklist == null) { + public void revertList(String category, String resourceName) { + ResourceList list = this.getList(category, resourceName); + if (list == null) { return; } - this.addressBlacklist.revert(); + list.revert(); } - public String getBlacklistJSONString() { - if (this.addressBlacklist == null) { + public String getJSONStringForList(String category, String resourceName) { + ResourceList list = this.getList(category, resourceName); + if (list == null) { return null; } - return this.addressBlacklist.getJSONString(); + return list.getJSONString(); } } diff --git a/src/main/java/org/qortal/transaction/ChatTransaction.java b/src/main/java/org/qortal/transaction/ChatTransaction.java index 2202d44a..8371b5b7 100644 --- a/src/main/java/org/qortal/transaction/ChatTransaction.java +++ b/src/main/java/org/qortal/transaction/ChatTransaction.java @@ -146,7 +146,7 @@ public class ChatTransaction extends Transaction { // Check for blacklisted author by address ResourceListManager listManager = ResourceListManager.getInstance(); - if (listManager.isAddressInBlacklist(this.chatTransactionData.getSender())) { + if (listManager.listContains("blacklist", "address", this.chatTransactionData.getSender())) { return ValidationResult.ADDRESS_IN_BLACKLIST; }