[NO ISSUE] Restrict UDF modification

Change-Id: I2cc23138793ae562cfa42c841b3bc4202391d9a1
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/11003
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ian Maxon <imaxon@uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCUdfApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCUdfApiServlet.java
index fec0b38..9efb6f8 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCUdfApiServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCUdfApiServlet.java
@@ -20,13 +20,13 @@
 
 import static org.apache.asterix.api.http.server.ServletConstants.SYS_AUTH_HEADER;
 import static org.apache.asterix.common.library.LibraryDescriptor.FIELD_HASH;
-import static org.apache.hyracks.api.exceptions.IFormattedException.getError;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.net.InetAddress;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -295,14 +295,41 @@
         responseWriter.flush();
     }
 
+    protected boolean isRequestPermittedForWrite(IServletRequest request, IServletResponse response) {
+        if (!isRequestOnLoopback(request)) {
+            rejectForbidden(response);
+            return false;
+        }
+        return true;
+    }
+
+    protected boolean isRequestOnLoopback(IServletRequest request) {
+        if (request.getLocalAddress() != null && request.getRemoteAddress() != null) {
+            InetAddress local = request.getLocalAddress().getAddress();
+            InetAddress remote = request.getRemoteAddress().getAddress();
+            return remote.isLoopbackAddress() && local.isLoopbackAddress();
+        } else {
+            return false;
+        }
+    }
+
+    protected static void rejectForbidden(IServletResponse response) {
+        response.setStatus(HttpResponseStatus.FORBIDDEN);
+        response.writer().write("{ \"error\": \"Forbidden\" }");
+    }
+
     @Override
     protected void post(IServletRequest request, IServletResponse response) {
-        handleModification(request, response, LibraryOperation.UPSERT);
+        if (isRequestPermittedForWrite(request, response)) {
+            handleModification(request, response, LibraryOperation.UPSERT);
+        }
     }
 
     @Override
     protected void delete(IServletRequest request, IServletResponse response) {
-        handleModification(request, response, LibraryOperation.DELETE);
+        if (isRequestPermittedForWrite(request, response)) {
+            handleModification(request, response, LibraryOperation.DELETE);
+        }
     }
 
 }
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
index f347b77..28b7fb3 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
@@ -2471,6 +2471,23 @@
                         + cUnit.getName() + "_qbc.adm");
     }
 
+    protected URI createLocalOnlyEndpointURI(String pathAndQuery) throws URISyntaxException {
+        InetSocketAddress endpoint;
+        if (!ncEndPointsList.isEmpty() && (pathAndQuery.equals(Servlets.QUERY_SERVICE)
+                || pathAndQuery.startsWith(Servlets.getAbsolutePath(Servlets.UDF)))) {
+            int endpointIdx = Math.abs(endpointSelector++ % ncEndPointsList.size());
+            endpoint = ncEndPointsList.get(endpointIdx);
+        } else if (isCcEndPointPath(pathAndQuery)) {
+            int endpointIdx = Math.abs(endpointSelector++ % endpoints.size());
+            endpoint = endpoints.get(endpointIdx);
+        } else {
+            throw new IllegalArgumentException("Invalid local endpoint format");
+        }
+        URI uri = URI.create("http://" + toHostPort("localhost", endpoint.getPort()) + pathAndQuery);
+        LOGGER.debug("Created endpoint URI: " + uri);
+        return uri;
+    }
+
     protected URI createEndpointURI(String pathAndQuery) throws URISyntaxException {
         InetSocketAddress endpoint;
         if (!ncEndPointsList.isEmpty() && (pathAndQuery.equals(Servlets.QUERY_SERVICE)