forked from Qortal/qortal
"Lists" feature is now generic.
This means that no additional structural code is required to add new lists. The only non-generic aspect are the API endpoints - it's best to keep these specific until we have a need for user-created lists.
This commit is contained in:
parent
bada4fd140
commit
c6d868d981
@ -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<String> addresses;
|
||||
@Schema(description = "A list of items")
|
||||
public List<String> items;
|
||||
|
||||
public AddressListRequest() {
|
||||
public ListRequest() {
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public class ResourceList {
|
||||
|
||||
private String category;
|
||||
private String resourceName;
|
||||
private List<String> list;
|
||||
private List<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<ResourceList> 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) {
|
||||
try {
|
||||
this.addressBlacklist.add(address);
|
||||
if (save) {
|
||||
this.addressBlacklist.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;
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (IllegalStateException | IOException e) {
|
||||
LOGGER.info("Unable to add address to blacklist", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeAddressFromBlacklist(String address, boolean save) {
|
||||
// List doesn't exist in array yet, so create it
|
||||
// This will load any existing data from the filesystem
|
||||
try {
|
||||
this.addressBlacklist.remove(address);
|
||||
ResourceList list = new ResourceList(category, resourceName);
|
||||
this.lists.add(list);
|
||||
return list;
|
||||
|
||||
if (save) {
|
||||
this.addressBlacklist.save();
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (IllegalStateException | IOException e) {
|
||||
LOGGER.info("Unable to remove address from blacklist", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAddressInBlacklist(String address) {
|
||||
if (this.addressBlacklist == null) {
|
||||
return false;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
public String getBlacklistJSONString() {
|
||||
if (this.addressBlacklist == null) {
|
||||
LOGGER.info("Unable to load or create list {} {}: {}", category, resourceName, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
return this.addressBlacklist.getJSONString();
|
||||
|
||||
}
|
||||
|
||||
public boolean addToList(String category, String resourceName, String item, boolean save) {
|
||||
ResourceList list = this.getList(category, resourceName);
|
||||
if (list == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
list.add(item);
|
||||
if (save) {
|
||||
list.save();
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (IllegalStateException | IOException e) {
|
||||
LOGGER.info(String.format("Unable to add item %s to list %s", item, list), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeFromList(String category, String resourceName, String item, boolean save) {
|
||||
ResourceList list = this.getList(category, resourceName);
|
||||
if (list == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
list.remove(item);
|
||||
|
||||
if (save) {
|
||||
list.save();
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (IllegalStateException | IOException e) {
|
||||
LOGGER.info(String.format("Unable to remove item %s from list %s", item, list), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean listContains(String category, String resourceName, String address) {
|
||||
ResourceList list = this.getList(category, resourceName);
|
||||
if (list == null) {
|
||||
return false;
|
||||
}
|
||||
return list.contains(address);
|
||||
}
|
||||
|
||||
public void saveList(String category, String resourceName) {
|
||||
ResourceList list = this.getList(category, resourceName);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
list.save();
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Unable to save list {} - reverting back to last saved state", list);
|
||||
list.revert();
|
||||
}
|
||||
}
|
||||
|
||||
public void revertList(String category, String resourceName) {
|
||||
ResourceList list = this.getList(category, resourceName);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
list.revert();
|
||||
}
|
||||
|
||||
public String getJSONStringForList(String category, String resourceName) {
|
||||
ResourceList list = this.getList(category, resourceName);
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
return list.getJSONString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user