From 9fdc901b7a0344fa2c6a708d0617dc21bcdbf10b Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sat, 7 Aug 2021 11:31:45 +0100 Subject: [PATCH] Added POST /lists/blacklist/addresses and DELETE /lists/blacklist/addresses API endpoints. These are the same as the /lists/blacklist/address/{address} endpoints but allow a JSON array of addresses to be specified in the request body. They currently return true if --- .../qortal/api/model/AddressListRequest.java | 18 +++ .../qortal/api/resource/ListsResource.java | 153 +++++++++++++++++- .../java/org/qortal/list/ResourceList.java | 11 ++ .../org/qortal/list/ResourceListManager.java | 34 +++- 4 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/qortal/api/model/AddressListRequest.java diff --git a/src/main/java/org/qortal/api/model/AddressListRequest.java b/src/main/java/org/qortal/api/model/AddressListRequest.java new file mode 100644 index 00000000..c600609f --- /dev/null +++ b/src/main/java/org/qortal/api/model/AddressListRequest.java @@ -0,0 +1,18 @@ +package org.qortal.api.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import java.util.List; + +@XmlAccessorType(XmlAccessType.FIELD) +public class AddressListRequest { + + @Schema(description = "A list of addresses") + public List addresses; + + public AddressListRequest() { + } + +} diff --git a/src/main/java/org/qortal/api/resource/ListsResource.java b/src/main/java/org/qortal/api/resource/ListsResource.java index 0f243b5a..70918a38 100644 --- a/src/main/java/org/qortal/api/resource/ListsResource.java +++ b/src/main/java/org/qortal/api/resource/ListsResource.java @@ -3,10 +3,12 @@ package org.qortal.api.resource; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; 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.crypto.Crypto; import org.qortal.data.account.AccountData; import org.qortal.list.ResourceListManager; @@ -50,7 +52,7 @@ public class ListsResource { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.ADDRESS_UNKNOWN); // Valid address, so go ahead and blacklist it - boolean success = ResourceListManager.getInstance().addAddressToBlacklist(address); + boolean success = ResourceListManager.getInstance().addAddressToBlacklist(address, true); return success ? "true" : "false"; } catch (DataException e) { @@ -58,6 +60,78 @@ public class ListsResource { } } + @POST + @Path("/blacklist/addresses") + @Operation( + summary = "Add one or more QORT addresses to the local blacklist", + requestBody = @RequestBody( + required = true, + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema( + implementation = AddressListRequest.class + ) + ) + ), + responses = { + @ApiResponse( + description = "Returns true if all addresses were processed, false if any couldn't be " + + "processed, or an exception on failure. If false or an exception is returned, " + + "the list will not be updated, and the request will need to be re-issued.", + content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "boolean")) + ) + } + ) + @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE}) + public String addAddressesToBlacklist(AddressListRequest addressListRequest) { + if (addressListRequest == null || addressListRequest.addresses == null) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA); + } + + int successCount = 0; + int errorCount = 0; + + try (final Repository repository = RepositoryManager.getRepository()) { + + for (String address : addressListRequest.addresses) { + + if (!Crypto.isValidAddress(address)) { + errorCount++; + continue; + } + + AccountData accountData = repository.getAccountRepository().getAccount(address); + // Not found? + if (accountData == null) { + errorCount++; + continue; + } + + // Valid address, so go ahead and blacklist it + boolean success = ResourceListManager.getInstance().addAddressToBlacklist(address, false); + if (success) { + successCount++; + } + else { + errorCount++; + } + } + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + + if (successCount > 0 && errorCount == 0) { + // All were successful, so save the blacklist + ResourceListManager.getInstance().saveBlacklist(); + return "true"; + } + else { + // Something went wrong, so revert + ResourceListManager.getInstance().revertBlacklist(); + return "false"; + } + } + @DELETE @Path("/blacklist/address/{address}") @@ -82,7 +156,7 @@ public class ListsResource { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.ADDRESS_UNKNOWN); // Valid address, so go ahead and blacklist it - boolean success = ResourceListManager.getInstance().removeAddressFromBlacklist(address); + boolean success = ResourceListManager.getInstance().removeAddressFromBlacklist(address, true); return success ? "true" : "false"; } catch (DataException e) { @@ -90,10 +164,83 @@ public class ListsResource { } } + @DELETE + @Path("/blacklist/addresses") + @Operation( + summary = "Remove one or more QORT addresses from the local blacklist", + requestBody = @RequestBody( + required = true, + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema( + implementation = AddressListRequest.class + ) + ) + ), + responses = { + @ApiResponse( + description = "Returns true if all addresses were processed, false if any couldn't be " + + "processed, or an exception on failure. If false or an exception is returned, " + + "the list will not be updated, and the request will need to be re-issued.", + content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "boolean")) + ) + } + ) + @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE}) + public String removeAddressesFromBlacklist(AddressListRequest addressListRequest) { + if (addressListRequest == null || addressListRequest.addresses == null) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA); + } + + int successCount = 0; + int errorCount = 0; + + try (final Repository repository = RepositoryManager.getRepository()) { + + for (String address : addressListRequest.addresses) { + + if (!Crypto.isValidAddress(address)) { + errorCount++; + continue; + } + + AccountData accountData = repository.getAccountRepository().getAccount(address); + // Not found? + if (accountData == null) { + errorCount++; + continue; + } + + // 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); + if (success) { + successCount++; + } + else { + errorCount++; + } + } + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + + if (successCount > 0 && errorCount == 0) { + // All were successful, so save the blacklist + ResourceListManager.getInstance().saveBlacklist(); + return "true"; + } + else { + // Something went wrong, so revert + ResourceListManager.getInstance().revertBlacklist(); + return "false"; + } + } + @GET @Path("/blacklist/address/{address}") @Operation( - summary = "Checks if an address is present in the local blacklist", + 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", diff --git a/src/main/java/org/qortal/list/ResourceList.java b/src/main/java/org/qortal/list/ResourceList.java index 740b23d6..1f0fb52b 100644 --- a/src/main/java/org/qortal/list/ResourceList.java +++ b/src/main/java/org/qortal/list/ResourceList.java @@ -17,6 +17,8 @@ import java.util.List; public class ResourceList { + private static final Logger LOGGER = LogManager.getLogger(ResourceList.class); + private String category; private String resourceName; private List list; @@ -84,6 +86,15 @@ public class ResourceList { return true; } + public boolean revert() { + try { + return this.load(); + } catch (IOException e) { + LOGGER.info("Unable to revert {} {}", this.resourceName, this.category); + } + return false; + } + /* List management */ diff --git a/src/main/java/org/qortal/list/ResourceListManager.java b/src/main/java/org/qortal/list/ResourceListManager.java index 375cddf8..0a7acfe1 100644 --- a/src/main/java/org/qortal/list/ResourceListManager.java +++ b/src/main/java/org/qortal/list/ResourceListManager.java @@ -2,7 +2,6 @@ package org.qortal.list; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.eclipse.jetty.util.IO; import java.io.IOException; @@ -29,10 +28,12 @@ public class ResourceListManager { return instance; } - public boolean addAddressToBlacklist(String address) { + public boolean addAddressToBlacklist(String address, boolean save) { try { this.addressBlacklist.add(address); - this.addressBlacklist.save(); + if (save) { + this.addressBlacklist.save(); + } return true; } catch (IllegalStateException | IOException e) { @@ -41,10 +42,13 @@ public class ResourceListManager { } } - public boolean removeAddressFromBlacklist(String address) { + public boolean removeAddressFromBlacklist(String address, boolean save) { try { this.addressBlacklist.remove(address); - this.addressBlacklist.save(); + + if (save) { + this.addressBlacklist.save(); + } return true; } catch (IllegalStateException | IOException e) { @@ -60,4 +64,24 @@ public class ResourceListManager { return this.addressBlacklist.contains(address); } + public void saveBlacklist() { + if (this.addressBlacklist == null) { + return; + } + + try { + this.addressBlacklist.save(); + } catch (IOException e) { + LOGGER.info("Unable to save blacklist - reverting back to last saved state"); + this.addressBlacklist.revert(); + } + } + + public void revertBlacklist() { + if (this.addressBlacklist == null) { + return; + } + this.addressBlacklist.revert(); + } + }