Index: /main/trunk/greenstone3/build.properties.svn
===================================================================
--- /main/trunk/greenstone3/build.properties.svn	(revision 36878)
+++ /main/trunk/greenstone3/build.properties.svn	(revision 36879)
@@ -22,4 +22,9 @@
 # but not remotely
 tomcat.server=localhost
+
+# If your machine's IP doesn't resolve to any of the usual local IP addresses,
+# and instead has any specific IP(s) associated with it, set or add them here within
+# the round brackets, with a vertical bar to connect them with the rest of the line
+tomcat.server.IPregex=(127\\\\.\\\\d+\\\\.\\\\d+\\\\.\\\\d+|::1|0:0:0:0:0:0:0:1(%\\\\d)?)
 
 # server.protocols must contain 'http' or 'https' or both (in order of preference) separated by commas
Index: /main/trunk/greenstone3/build.xml
===================================================================
--- /main/trunk/greenstone3/build.xml	(revision 36878)
+++ /main/trunk/greenstone3/build.xml	(revision 36879)
@@ -1878,5 +1878,7 @@
     <filter token="localhost.port.http" value="${localhost.port.http}"/>
     <filter token="tomcat.port.https" value="${tomcat.port.https}"/>
-	<filter token="restrict.http.to.local" value="${restrict.http.to.local}"/>
+    <filter token="restrict.http.to.local" value="${restrict.http.to.local}"/>
+    <filter token="tomcat.server.IPregex" value="${tomcat.server.IPregex}"/>
+
     <filter token="greenstone.context" value="${greenstone.context}"/>
     <filter token="solr.context" value="${solr.context}"/>
Index: /main/trunk/greenstone3/resources/web/global.properties.svn
===================================================================
--- /main/trunk/greenstone3/resources/web/global.properties.svn	(revision 36878)
+++ /main/trunk/greenstone3/resources/web/global.properties.svn	(revision 36879)
@@ -29,4 +29,5 @@
 localhost.port.http=@localhost.port.http@
 restrict.http.to.local=@restrict.http.to.local@
+tomcat.server.IPregex=@tomcat.server.IPregex@
 
 
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/LibraryServlet.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/LibraryServlet.java	(revision 36878)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/LibraryServlet.java	(revision 36879)
@@ -562,4 +562,10 @@
 	should_cache = false;
 
+	// System commands now need to be logged in/have permissions
+	// like configuring, activation and deactivation
+	if(!configureSecurityCheck(request, userContext, out, baseURL, lang, output, queryMap)) {
+	    return;
+	}
+	
 	// we may want to remove all collection cache info, or just a specific collection
 	boolean clean_all = true;
@@ -575,5 +581,5 @@
 	  }
 	else
-	  {
+	  {	      
 	    // check other system types
 	    if (subaction.equals("a") || subaction.equals("d"))
@@ -1074,4 +1080,144 @@
     }
 
+    /**
+     * request.getRemoteAddr() returns the IP of the client *or* of a proxy.
+     * We want the client IP, so we can check if it's truly local or not, since proxies can be
+     * local, thus masking a client connecting from an external IP.
+     * The following code is from
+     * https://www.javaprogramto.com/2020/11/how-to-get-client-ip-address-in-java.html
+     */
+    public String getClientIpAddress(HttpServletRequest request)
+    {
+	final String[] VALID_IP_HEADER_CANDIDATES = { 
+	    "X-Forwarded-For",
+	    "Proxy-Client-IP",
+	    "WL-Proxy-Client-IP",
+	    "HTTP_X_FORWARDED_FOR",
+	    "HTTP_X_FORWARDED",
+	    "HTTP_X_CLUSTER_CLIENT_IP",
+	    "HTTP_CLIENT_IP",
+	    "HTTP_FORWARDED_FOR",
+	    "HTTP_FORWARDED",
+	    "HTTP_VIA",
+	    "REMOTE_ADDR"
+	};
+	
+	
+	for (String header : VALID_IP_HEADER_CANDIDATES) {
+	    String ipAddress = request.getHeader(header);
+	    if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
+		return ipAddress;
+	    }
+	}
+	return request.getRemoteAddr(); // fallback to whatever is the incoming IP (could be proxy)
+    }
+
+
+    private void debugCheckAgainstRegex() {
+	
+	// Load the global.properties file, get the tomcat.server.IPregex and check for any of it
+	// matching against the local IP
+	String tomcatServerIPregex = GlobalProperties.getProperty("tomcat.server.IPregex", "");
+	
+	logger.info("@@@ tomcatServerIPregex: " + tomcatServerIPregex);
+	
+	String[] testIPs = {"127.0.0.1", "::1", "0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:1%3"};
+	
+	//if(ipOfClient.equals(request.getLocalAddr()) || ipOfClient.equals("127.0.0.1")
+	//   || ipOfClient.equals("::1") || ipOfClient.startsWith("0:0:0:0:0:0:0:1")) {
+	
+	for(String testIP : testIPs) {
+	    if(testIP.matches(tomcatServerIPregex)) {
+		logger.info("testIP " + testIP + " matches regex.");
+	    } else {
+		logger.info("testIP " + testIP + " DOESN'T match regex.");
+	    }
+	}	
+    }
+    
+    
+    /**
+     * Clients on the same machine as the GS3 server can reconfigure and activate/deactivate,
+     * Users in the administrator group also have the right to do such system commands.
+     * For collection-level system commands, users with permissions to edit all collections or
+     * the specific collection have a right to activate/deactivate a collection.
+     * Useful links:
+     * https://stackoverflow.com/questions/13563351/how-to-restrict-acces-to-specific-servlet-by-ip-via-container-configuration
+     * https://stackoverflow.com/questions/35015514/restricting-access-to-localhost-for-java-servlet-endpoint
+     * Reverse of: https://stackoverflow.com/questions/2539461/how-to-access-java-servlet-running-on-my-pc-from-outside
+    */
+    private boolean configureSecurityCheck(HttpServletRequest request, UserContext userContext,
+					   PrintWriter out, String baseURL, String lang,
+					   String output, Map<String, String[]> queryMap)
+    {
+	// if request emanates on same machine as GS server/from local machine, let it go through
+	String ipOfClient = getClientIpAddress(request); //request.getRemoteAddr();
+	logger.info("@@@ Request local IP: " + request.getLocalAddr());
+	logger.info("@@@ Client/remote IP: " + ipOfClient);
+	
+	//debugCheckAgainstRegex();
+
+	//if(ipOfClient.equals(request.getLocalAddr()) || ipOfClient.equals("127.0.0.1")
+	//   || ipOfClient.equals("::1") || ipOfClient.startsWith("0:0:0:0:0:0:0:1")) {
+	//    return true;
+	//}	
+	
+	// Load the global.properties file, get the tomcat.server.IPregex and check for any of it
+	// matching against the local IP
+	String tomcatServerIPregex = GlobalProperties.getProperty("tomcat.server.IPregex", "");	
+	if(ipOfClient.equals(request.getLocalAddr()) || ipOfClient.matches(tomcatServerIPregex)) {
+	    return true;
+	}
+	
+	// TODO: Want regex for localhost situations and then make it a property in build.props
+	// In build.props: comment, you might like to leave the IP of your server machine
+	// at the end of the line	
+	// Check with another machine: browser, retest cmdline and activate.pl
+	// Final phase is testing with apache set up
+	
+	// if we have sc=colname, user needs to be colname-|all-collection-editor
+	// if we have st=collection&sn=colname, same as above
+	// otherwise user needs to be administrator
+
+	// We can have MODULE_TYPE=collection And MODULE_NAME=collname,
+	// OR SYSTEM_CLUSTER=collname
+	// If none of these specified, then assume the user should be administrator
+	// If not a collection level operation, the user should be administrator
+
+	String coll = null;
+	String module_type = getFirstParam(GSParams.SYSTEM_MODULE_TYPE, queryMap);
+	if(module_type != null && module_type.equals("collection")) {
+	    // To deactivate demo:library?excerptid=gs_content&a=s&sa=d&sc=lucene-jdbm-demo
+	    coll = getFirstParam(GSParams.SYSTEM_MODULE_NAME, queryMap);
+	} else {
+	    // To reconfigure demo: library?excerptid=gs_content&a=s&sa=c&sc=lucene-jdbm-demo
+	    coll = getFirstParam(GSParams.SYSTEM_CLUSTER, queryMap);
+	    if (coll != null && coll.equals("")) {
+		coll = null;
+	    }
+	}
+	String collname_editor = (coll == null) ? null : (coll+"-collections-editor");
+
+	for (String group : userContext.getGroups()) {
+
+	    // if user is admin, all is good: they can do reconfigure (on service, collection or
+	    // any module) or activate/deactivate a collection or any module
+	    if (group.equals("administrator")) {		
+		return true;
+	    }
+
+	    // if collection level operation, user must have permissions for the collection
+	    // so the user must be in at least one of all-|personal-|collname-collections-editor
+	    if (coll != null) { // collname_editor won't be null
+		if(group.equals("all-collections-editor") || group.equals(collname_editor)) {
+		    return true; // do we add || group.equals("personal-collections-editor")?
+		}
+	    }
+	    
+	}
+
+	errorAndLoginPage(request, userContext, out, baseURL, lang, output);
+	return false;	
+    }
 
     private boolean runCollectionSecurityCheck(HttpServletRequest request, UserContext userContext, PrintWriter out, String baseURL, String collection, String document, String lang, String output) {
@@ -1112,25 +1258,5 @@
 	    if (!found)
 	    {
-
-		String error_message = "";
-		if (request.getAuthType() == null)
-		{
-		    error_message = getTextString("auth.error.login", lang);
-		}
-		else
-		{
-		    error_message = getTextString("auth.error.incorrect_login", lang);
-		}
-		
-		HttpSession session = request.getSession();
-		ServletContext context = session.getServletContext();
-		String redirect_url_string = request.getRequestURI().replaceFirst(context.getContextPath() + "/", "");
-
-		if (request.getQueryString() != null && request.getQueryString().length() > 0)
-		{
-		    redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
-		}
-
-		generateLoginPage(redirect_url_string, error_message, userContext, out, baseURL, output);
+		errorAndLoginPage(request, userContext, out, baseURL, lang, output);
 		return false;
 	    }
@@ -1139,4 +1265,31 @@
     }
 
+
+    private void errorAndLoginPage(HttpServletRequest request, UserContext userContext,
+				   PrintWriter out, String baseURL, String lang, String output)
+    {	
+	String error_message = "";
+	if (request.getAuthType() == null)
+	    {
+		error_message = getTextString("auth.error.login", lang);
+	    }
+	else
+	    {
+		error_message = getTextString("auth.error.incorrect_login", lang);
+	    }
+	
+	HttpSession session = request.getSession();
+	ServletContext context = session.getServletContext();
+	String redirect_url_string = request.getRequestURI().replaceFirst(context.getContextPath() + "/", "");
+	
+	if (request.getQueryString() != null && request.getQueryString().length() > 0)
+	    {
+		redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
+	    }
+	
+	generateLoginPage(redirect_url_string, error_message, userContext, out, baseURL, output);
+    }
+
+    
   private String getTextString(String key, String lang) {
       return Dictionary.getTextString(key, lang, null, null, null, null, null);
