diff --git a/doc/release-notes/6.12-release-notes.md b/doc/release-notes/6.12-release-notes.md
index b50a5be5ebf..a3b6243233e 100644
--- a/doc/release-notes/6.12-release-notes.md
+++ b/doc/release-notes/6.12-release-notes.md
@@ -41,7 +41,7 @@ These are features that weren't already mentioned under "highlights" above.
## Other Changes
-- Payara 7.2026.8 is now the supported and recommended version. See upgrade instructions below and #12498.
+- Payara 7.2026.9 is now the supported and recommended version. See upgrade instructions below, #12498 and #12715.
- Database indexes were added to the guestbookresponse table to speed up queries. See #12616.
- The QA Guide was added as an experiment in #10103 but has been removed because it has proved less useful than imagined. It can still be [viewed](https://guides.dataverse.org/en/6.11/qa/index.html) as it existed as of Dataverse 6.11. See #12582.
@@ -164,7 +164,7 @@ If you are running Payara as a non-root user (and you should be!), **remember no
Also, we assume that Payara is installed in `/usr/local/payara7`. If not, adjust as needed.
-The instructions below describe the upgrade procedure based on moving your existing Payara 7.2026.2 domain directory into the new Payara 7.2026.8 distribution. We recommend this method because it is the easiest way to recreate your current configuration and preserve your data.
+The instructions below describe the upgrade procedure based on moving your existing Payara 7.2026.2 domain directory into the new Payara 7.2026.9 distribution. We recommend this method because it is the easiest way to recreate your current configuration and preserve your data.
1. Undeploy Dataverse, if deployed, using the unprivileged service account ("dataverse", by default).
@@ -188,12 +188,12 @@ The instructions below describe the upgrade procedure based on moving your exist
sudo mv /usr/local/payara7 /usr/local/payara7-2026.2
```
-1. Download the new Payara version 7.2026.8, and unzip it.
+1. Download the new Payara version 7.2026.9, and unzip it.
```shell
- curl -L -O https://nexus.payara.fish/repository/payara-community/fish/payara/distributions/payara/7.2026.8/payara-7.2026.8.zip
+ curl -L -O https://nexus.payara.fish/repository/payara-community/fish/payara/distributions/payara/7.2026.9/payara-7.2026.9.zip
- sudo unzip payara-7.2026.8.zip -d /usr/local/
+ sudo unzip payara-7.2026.9.zip -d /usr/local/
```
1. Set permission for the service account ("dataverse" by default).
@@ -223,7 +223,7 @@ The instructions below describe the upgrade procedure based on moving your exist
1. Update configuration files in the moved domain.
- The file `glassfish-acc.xml` from Payara 7.2026.2 has been renamed to `payara-acc.xml` in Payara 7.2026.8. Also `default-logging.properties` has been updated in the Payara 7.2026.8. We recommend copying these new files from the Payara 7.2026.8 distribution domain into the domain you copied over from your Payara 7.2026.2 installation.
+ The file `glassfish-acc.xml` from Payara 7.2026.2 has been renamed to `payara-acc.xml` in Payara 7.2026.9. Also `default-logging.properties` has been updated in the Payara 7.2026.9. We recommend copying these new files from the Payara 7.2026.9 distribution domain into the domain you copied over from your Payara 7.2026.2 installation.
```shell
sudo rm /usr/local/payara7/glassfish/domains/domain1/config/glassfish-acc.xml
diff --git a/doc/sphinx-guides/source/installation/prerequisites.rst b/doc/sphinx-guides/source/installation/prerequisites.rst
index 1d7cfa99081..00203b77d9b 100644
--- a/doc/sphinx-guides/source/installation/prerequisites.rst
+++ b/doc/sphinx-guides/source/installation/prerequisites.rst
@@ -44,7 +44,7 @@ On RHEL/derivative you can make Java 21 the default with the ``alternatives`` co
Payara
------
-Payara 7.2026.8 is recommended. Newer versions might work fine. Regular updates are recommended.
+Payara 7.2026.9 is recommended. Newer versions might work fine. Regular updates are recommended.
Installing Payara
=================
@@ -55,8 +55,8 @@ Installing Payara
- Download and install Payara (installed in ``/usr/local/payara7`` in the example commands below)::
- # wget https://nexus.payara.fish/repository/payara-community/fish/payara/distributions/payara/7.2026.8/payara-7.2026.8.zip
- # unzip payara-7.2026.8.zip
+ # wget https://nexus.payara.fish/repository/payara-community/fish/payara/distributions/payara/7.2026.9/payara-7.2026.9.zip
+ # unzip payara-7.2026.9.zip
# mv payara7 /usr/local
If nexus.payara.fish is ever down for maintenance, Payara distributions are also available from https://repo1.maven.org/maven2/fish/payara/distributions/payara/
diff --git a/modules/dataverse-parent/pom.xml b/modules/dataverse-parent/pom.xml
index 2cd63f57da0..49feb52b7bb 100644
--- a/modules/dataverse-parent/pom.xml
+++ b/modules/dataverse-parent/pom.xml
@@ -149,7 +149,7 @@
-Duser.timezone=${project.timezone} -Dfile.encoding=${project.build.sourceEncoding} -Duser.language=${project.language} -Duser.region=${project.region}
- 7.2026.8
+ 7.2026.9
42.7.12
9.8.0
16
diff --git a/src/main/java/edu/harvard/iq/dataverse/api/filter/ApiBlockingFeature.java b/src/main/java/edu/harvard/iq/dataverse/api/filter/ApiBlockingFeature.java
new file mode 100644
index 00000000000..d5651f01e55
--- /dev/null
+++ b/src/main/java/edu/harvard/iq/dataverse/api/filter/ApiBlockingFeature.java
@@ -0,0 +1,250 @@
+package edu.harvard.iq.dataverse.api.filter;
+
+import edu.harvard.iq.dataverse.authorization.groups.impl.ipaddress.ip.IpAddress;
+import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
+import edu.harvard.iq.dataverse.settings.JvmSettings;
+import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
+import edu.harvard.iq.dataverse.util.json.JsonUtil;
+import edu.harvard.iq.dataverse.validation.PasswordValidatorServiceBean;
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import jakarta.json.JsonObject;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.DynamicFeature;
+import jakarta.ws.rs.container.ResourceInfo;
+import jakarta.ws.rs.core.FeatureContext;
+import jakarta.ws.rs.ext.Provider;
+import org.eclipse.jetty.util.StringUtil;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+/**
+ * A DynamicFeature that registers ApiBlockingFilter for Dataverse API endpoints.
+ * This approach avoids the need to inject ResourceInfo at request time, which
+ * is no longer supported via @Context in Jakarta EE 11 / Payara 7.
+ */
+@Provider
+public class ApiBlockingFeature implements DynamicFeature {
+
+ private static final Logger logger = Logger.getLogger(ApiBlockingFeature.class.getName());
+
+ @Inject
+ private SettingsServiceBean settingsService;
+
+ @Inject
+ private PasswordValidatorServiceBean passwordValidatorService;
+
+ @Inject
+ private HttpServletRequest httpServletRequest;
+
+ // Policies
+ private static final String DROP = "drop";
+ private static final String LOCALHOST_ONLY = "localhost-only";
+ private static final String UNBLOCK_KEY = "unblock-key";
+
+ private static final Map POLICY_ERROR_MESSAGES = new HashMap<>();
+ static {
+ POLICY_ERROR_MESSAGES.put(DROP, "Endpoint blocked. Access denied.");
+ POLICY_ERROR_MESSAGES.put(LOCALHOST_ONLY, "Endpoint restricted to localhost access only.");
+ POLICY_ERROR_MESSAGES.put(UNBLOCK_KEY, "Endpoint requires an unblock key for access.");
+ }
+
+ private String policy = null;
+ private JsonObject errorJson = null;
+ private volatile List blockedApiEndpointPatterns = new ArrayList<>();
+ private String key;
+
+ // If any of the JvmSettings are not set, revert to checking the db settings on every call
+ private boolean checkSettings = false;
+ private String endpointList = null;
+
+ @PostConstruct
+ public void init() {
+ // Check JvmSettings first for BlockedApiPolicy
+ policy = JvmSettings.API_BLOCKED_POLICY.lookupOptional().orElse(settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiPolicy, DROP));
+
+ if(!(DROP.equals(policy) || LOCALHOST_ONLY.equals(policy) || UNBLOCK_KEY.equals(policy))) {
+ logger.severe("Invalid BlockedApiPolicy setting: " + policy + ". Using policy 'drop'");
+ policy = DROP;
+ }
+ Optional jvmEndpointList = JvmSettings.API_BLOCKED_ENDPOINTS.lookupOptional();
+ if (!jvmEndpointList.isPresent()) {
+ checkSettings = true;
+ }
+ endpointList = jvmEndpointList
+ .orElse(settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiEndpoints, ""));
+ logger.info("Using policy: " + policy + " to block API endpoints: " + endpointList);
+ if (!(endpointList.contains("admin") && endpointList.contains("builtin-users"))) {
+ logger.warning(
+ "Not blocking admin and builtin-user endpoints is a security issue unless you are blocking them in an external proxy.");
+ }
+ if (UNBLOCK_KEY.equals(policy)) {
+ Optional jvmKey = JvmSettings.API_BLOCKED_KEY.lookupOptional();
+ if (!jvmKey.isPresent()) {
+ checkSettings = true;
+ }
+ key = jvmKey.orElse(settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiKey));
+ if (StringUtil.isBlank(key)) {
+ logger.severe(
+ "Using unblock-key policy and no unblock key found in JvmSettings.API_BLOCKED_KEY or SettingsService.BlockedApiKey");
+ } else if (passwordValidatorService.validate(key).size() == 0) {
+ logger.warning("Weak unblock key detected. Please use a stronger key for better security.");
+ }
+ }
+ updateBlockedPoints(endpointList);
+ if(checkSettings) {
+ logger.warning("Not all required dataverse.api.blocked.* settings not found. Dataverse use deprecated db settings and check for updates on every API call.");
+ }
+ }
+
+ @Override
+ public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+ Class> clazz = resourceInfo.getResourceClass();
+ Method method = resourceInfo.getResourceMethod();
+
+ if (clazz.getName().startsWith("edu.harvard.iq.dataverse")) {
+ String classPath = "";
+ String methodPath = "";
+
+ if (clazz.isAnnotationPresent(Path.class)) {
+ classPath = clazz.getAnnotation(Path.class).value();
+ }
+
+ if (method.isAnnotationPresent(Path.class)) {
+ methodPath = method.getAnnotation(Path.class).value();
+ }
+
+ String fullPath = (classPath + "/" + methodPath).replaceAll("//", "/");
+ context.register(new ApiBlockingFilter(this, fullPath));
+ }
+ }
+
+ public boolean shouldBlock(String fullPath, ContainerRequestContext requestContext) {
+ if (checkSettings) {
+ updateSettingsIfChanged();
+ }
+
+ boolean isBlockableEndpoint = false;
+ List currentPatterns = blockedApiEndpointPatterns;
+ for (Pattern blockedEndpointPattern : currentPatterns) {
+ if (blockedEndpointPattern.matcher(fullPath).matches()) {
+ isBlockableEndpoint = true;
+ break;
+ }
+ }
+
+ if (!isBlockableEndpoint) {
+ return false;
+ }
+
+ // Blockable endpoint - now check policy
+ return isBlocked(policy, requestContext);
+ }
+
+ private synchronized void updateSettingsIfChanged() {
+ // Backward compatibility, e.g. for setup scripts, dev environments where
+ // dynamic update from the db settings is expected
+ String newPolicy = settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiPolicy,
+ JvmSettings.API_BLOCKED_POLICY.lookupOptional().orElse(DROP));
+ String newEndpointList = settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiEndpoints,
+ JvmSettings.API_BLOCKED_ENDPOINTS.lookupOptional().orElse(""));
+
+ boolean changed = false;
+ if (!newPolicy.equals(policy)) {
+ policy = newPolicy;
+ changed = true;
+ }
+ if (!endpointList.equals(newEndpointList)) {
+ endpointList = newEndpointList;
+ changed = true;
+ }
+
+ if (changed) {
+ updateBlockedPoints(endpointList);
+ }
+
+ if (policy.equals(UNBLOCK_KEY)) {
+ key = settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiKey,
+ JvmSettings.API_BLOCKED_KEY.lookupOptional().orElse(""));
+ if (StringUtil.isBlank(key)) {
+ logger.severe(
+ "Using unblock-key policy and no unblock key found in JvmSettings.API_BLOCKED_KEY or SettingsService.BlockedApiKey");
+ }
+ }
+ }
+
+ private boolean isBlocked(String policy, ContainerRequestContext requestContext) {
+ switch (policy) {
+ case DROP:
+ return true;
+ case LOCALHOST_ONLY:
+ if (httpServletRequest == null) {
+ logger.warning("Unable to obtain HttpServletRequest");
+ return true;
+ }
+ IpAddress origin = new DataverseRequest(null, httpServletRequest).getSourceAddress();
+ if (!origin.isLocalhost()) {
+ return true;
+ }
+ break;
+ case UNBLOCK_KEY:
+ String providedKey = requestContext.getHeaderString(ApiBlockingFilter.UNBLOCK_KEY_HEADER);
+ if (StringUtil.isBlank(providedKey)) {
+ providedKey = requestContext.getUriInfo().getQueryParameters().getFirst(ApiBlockingFilter.UNBLOCK_KEY_QUERYPARAM);
+ }
+ // Must have a non-blank key defined and the query param must match it
+ if (StringUtil.isNotBlank(key) && key.equals(providedKey)) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void updateBlockedPoints(String endpointList) {
+ List newPatterns = new ArrayList<>();
+
+ String currentErrorMessage = POLICY_ERROR_MESSAGES.getOrDefault(policy,
+ "Endpoint blocked. Please contact the dataverse administrator.");
+
+ errorJson = JsonUtil.createObjectBuilder().add("status", "error").add("message", currentErrorMessage).build();
+
+ for (String endpoint : endpointList.split(",")) {
+ String endpointPrefix = canonicalize(endpoint);
+ if (!endpointPrefix.isEmpty()) {
+ logger.log(Level.INFO, "Blocking API endpoint: {0}", endpointPrefix);
+ newPatterns.add(Pattern.compile(convertPathToRegex(endpointPrefix)));
+ }
+ }
+ blockedApiEndpointPatterns = newPatterns;
+ }
+
+ private String convertPathToRegex(String path) {
+ return "^" + path.replaceAll("\\{[^}]+\\}", "[^/]+").replace("/", "\\/") + "(\\/.*)?$";
+ }
+
+ private String canonicalize(String in) {
+ in = in.trim();
+ if (in.startsWith("/")) {
+ in = in.substring(1);
+ }
+ if (in.endsWith("/")) {
+ in = in.substring(0, in.length() - 1);
+ }
+ return in;
+ }
+
+ public JsonObject getErrorJson() {
+ return errorJson;
+ }
+}
diff --git a/src/main/java/edu/harvard/iq/dataverse/api/filter/ApiBlockingFilter.java b/src/main/java/edu/harvard/iq/dataverse/api/filter/ApiBlockingFilter.java
index 84497b479d4..0285fae3410 100644
--- a/src/main/java/edu/harvard/iq/dataverse/api/filter/ApiBlockingFilter.java
+++ b/src/main/java/edu/harvard/iq/dataverse/api/filter/ApiBlockingFilter.java
@@ -1,246 +1,37 @@
package edu.harvard.iq.dataverse.api.filter;
-import edu.harvard.iq.dataverse.authorization.groups.impl.ipaddress.ip.IpAddress;
-import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
-import edu.harvard.iq.dataverse.settings.JvmSettings;
-import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
-import edu.harvard.iq.dataverse.util.json.JsonUtil;
-import edu.harvard.iq.dataverse.validation.PasswordValidatorServiceBean;
-import jakarta.annotation.PostConstruct;
-import jakarta.inject.Inject;
-import jakarta.json.Json;
-import jakarta.json.JsonObject;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.ws.rs.Path;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
-import jakarta.ws.rs.container.ResourceInfo;
-import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.ext.Provider;
import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.regex.Pattern;
-import org.eclipse.jetty.util.StringUtil;
-
-@Provider
+/**
+ * Filter registered by ApiBlockingFeature to block specific Dataverse API endpoints.
+ */
public class ApiBlockingFilter implements ContainerRequestFilter {
private static final Logger logger = Logger.getLogger(ApiBlockingFilter.class.getName());
public static final String UNBLOCK_KEY_QUERYPARAM = "unblock-key";
public static final String UNBLOCK_KEY_HEADER = "X-Dataverse-unblock-key";
- // Policies
- private static final String DROP = "drop";
- private static final String LOCALHOST_ONLY = "localhost-only";
- private static final String UNBLOCK_KEY = "unblock-key";
-
- private static final Map POLICY_ERROR_MESSAGES = new HashMap<>();
- static {
- POLICY_ERROR_MESSAGES.put(DROP, "Endpoint blocked. Access denied.");
- POLICY_ERROR_MESSAGES.put(LOCALHOST_ONLY, "Endpoint restricted to localhost access only.");
- POLICY_ERROR_MESSAGES.put(UNBLOCK_KEY, "Endpoint requires an unblock key for access.");
- }
-
- @Inject
- private SettingsServiceBean settingsService;
-
- @Inject
- private PasswordValidatorServiceBean passwordValidatorService;
-
- @Context
- private ResourceInfo resourceInfo;
-
- @Context
- private HttpServletRequest httpServletRequest;
-
- private String policy = null;
-
- private JsonObject errorJson = null;
-
- private List blockedApiEndpointPatterns = new ArrayList<>();
-
- private String key;
-
- // If any of the JvmSettings are not set, revert to checking the db settings on
- // every call
- private boolean checkSettings = false;
- private String endpointList = null;
-
- @PostConstruct
- public void init() {
- // Check JvmSettings first for BlockedApiPolicy
- policy = JvmSettings.API_BLOCKED_POLICY.lookupOptional().orElse(settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiPolicy, DROP));
-
- if(!(DROP.equals(policy) || LOCALHOST_ONLY.equals(policy) || UNBLOCK_KEY.equals(policy))) {
- logger.severe("Invalid BlockedApiPolicy setting: " + policy + ". Using policy 'drop'");
- policy = DROP;
- }
- Optional jvmEndpointList = JvmSettings.API_BLOCKED_ENDPOINTS.lookupOptional();
- if (!jvmEndpointList.isPresent()) {
- checkSettings = true;
- }
- endpointList = jvmEndpointList
- .orElse(settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiEndpoints, ""));
- logger.info("Using policy: " + policy + " to block API endpoints: " + endpointList);
- if (!(endpointList.contains("admin") && endpointList.contains("builtin-users"))) {
- logger.warning(
- "Not blocking admin and builtin-user endpoints is a security issue unless you are blocking them in an external proxy.");
- }
- if (UNBLOCK_KEY.equals(policy)) {
- Optional jvmKey = JvmSettings.API_BLOCKED_KEY.lookupOptional();
- if (!jvmKey.isPresent()) {
- checkSettings = true;
- }
- key = jvmKey.orElse(settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiKey));
- if (StringUtil.isBlank(key)) {
- logger.severe(
- "Using unblock-key policy and no unblock key found in JvmSettings.API_BLOCKED_KEY or SettingsService.BlockedApiKey");
- } else if (passwordValidatorService.validate(key).size() == 0) {
- logger.warning("Weak unblock key detected. Please use a stronger key for better security.");
- }
- }
- updateBlockedPoints(endpointList);
- if(checkSettings) {
- logger.warning("Not all required dataverse.api.blocked.* settings not found. Dataverse use deprecated db settings and check for updates on every API call.");
- }
+ private final ApiBlockingFeature feature;
+ private final String fullPath;
+ public ApiBlockingFilter(ApiBlockingFeature feature, String fullPath) {
+ this.feature = feature;
+ this.fullPath = fullPath;
}
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
-
- Method method = resourceInfo.getResourceMethod();
- Class> clazz = resourceInfo.getResourceClass();
-
- String classPath = "";
- String methodPath = "";
-
- if (clazz.isAnnotationPresent(Path.class)) {
- classPath = clazz.getAnnotation(Path.class).value();
- }
-
- if (method.isAnnotationPresent(Path.class)) {
- methodPath = method.getAnnotation(Path.class).value();
- }
-
- if (checkSettings) {
- // Backward compatibility, e.g. for setup scripts, dev environments where
- // dynamic update from the db settings is expected
- policy = settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiPolicy,
- JvmSettings.API_BLOCKED_POLICY.lookupOptional().orElse(DROP));
- String newEndpointList = settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiEndpoints,
- JvmSettings.API_BLOCKED_ENDPOINTS.lookupOptional().orElse(""));
- if (!endpointList.equals(newEndpointList)) {
- endpointList = newEndpointList;
- updateBlockedPoints(endpointList);
- }
- if (policy.equals(UNBLOCK_KEY)) {
- key = settingsService.getValueForKey(SettingsServiceBean.Key.BlockedApiKey,
- JvmSettings.API_BLOCKED_KEY.lookupOptional().orElse(""));
- if (StringUtil.isBlank(key)) {
- logger.severe(
- "Using unblock-key policy and no unblock key found in JvmSettings.API_BLOCKED_KEY or SettingsService.BlockedApiKey");
- }
- }
- }
- String fullPath = (classPath + "/" + methodPath).replaceAll("//", "/");
- logger.fine("Full path is " + fullPath);
-
- boolean isBlockableEndpoint = false;
- for (Pattern blockedEndpointPattern : blockedApiEndpointPatterns) {
- if (blockedEndpointPattern.matcher(fullPath).matches()) {
- isBlockableEndpoint = true;
- break;
- }
- }
- if (!isBlockableEndpoint) {
- return;
- }
- // Blocakble endpoint - now check policy
- if (isBlocked(policy, requestContext)) {
+ if (feature.shouldBlock(fullPath, requestContext)) {
logger.fine("Blocked " + fullPath);
- requestContext.abortWith(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity(errorJson)
- .type(jakarta.ws.rs.core.MediaType.APPLICATION_JSON).build());
- return;
- }
- }
-
- private boolean isBlocked(String policy, ContainerRequestContext requestContext) {
- switch (policy) {
- case DROP:
- return true;
- case LOCALHOST_ONLY:
- if (httpServletRequest == null) {
- logger.warning("Unable to obtain HttpServletRequest from ContainerRequestContext");
- // Handle the case where HttpServletRequest is not available
- return true;
- }
- IpAddress origin = new DataverseRequest(null, httpServletRequest).getSourceAddress();
- if (!origin.isLocalhost()) {
- return true;
- }
- break;
- case UNBLOCK_KEY:
- String providedKey = requestContext.getHeaderString(UNBLOCK_KEY_HEADER);
- if (StringUtil.isBlank(providedKey)) {
- providedKey = requestContext.getUriInfo().getQueryParameters().getFirst(UNBLOCK_KEY_QUERYPARAM);
- }
- // Must have a non-blank key defined and the query param must match it
- if (StringUtil.isNotBlank(key) && key.equals(providedKey)) {
- return false;
- }
- // Otherwise we have a blocked endpoint and the key doesn't work (not set or
- // doesn't match what's sent)
- return true;
- }
- return false;
- }
-
- private void updateBlockedPoints(String endpointList) {
- blockedApiEndpointPatterns.clear();
-
- String currentErrorMessage = POLICY_ERROR_MESSAGES.getOrDefault(policy,
- "Endpoint blocked. Please contact the dataverse administrator.");
-
- errorJson = JsonUtil.createObjectBuilder().add("status", "error").add("message", currentErrorMessage).build();
-
- for (String endpoint : endpointList.split(",")) {
- String endpointPrefix = canonicalize(endpoint);
- if (!endpointPrefix.isEmpty()) {
- logger.log(Level.INFO, "Blocking API endpoint: {0}", endpointPrefix);
- blockedApiEndpointPatterns.add(Pattern.compile(convertPathToRegex(endpointPrefix)));
- }
- }
- }
-
- private String convertPathToRegex(String path) {
- return "^" + path.replaceAll("\\{[^}]+\\}", "[^/]+").replace("/", "\\/") + "(\\/.*)?$";
- }
-
- /**
- * Creates a canonical representation of {@code in}: trimmed spaces and slashes
- *
- * @param in the raw string
- * @return {@code in} with no trailing and leading spaces and slashes.
- */
- private String canonicalize(String in) {
- in = in.trim();
- if (in.startsWith("/")) {
- in = in.substring(1);
- }
- if (in.endsWith("/")) {
- in = in.substring(0, in.length() - 1);
+ requestContext.abortWith(Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity(feature.getErrorJson())
+ .type(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
+ .build());
}
- return in;
}
}
\ No newline at end of file