Ignore:
Timestamp:
2018-09-06T22:32:58+12:00 (6 years ago)
Author:
ak19
Message:

solr should only be accessible locally (from localhost, specifically 127.0.0.1) which means over http. This conflicted with the previous design of the properties file for working with http and/or https. Now we have tomcat.port.https and localhost.port.http, both always set. In place of server.protocol that used to contain the default protocol, we now have server.protocols which can be set to a comma separated list of one or both of http and https. Drastic restructuring followed. I think I've tested all but https certification stuff.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/util/ProtocolPortProperties.java

    r32419 r32429  
    2222
    2323/*
    24  * Utility class to set the protocol and port values from the Properties provided,
     24 * Utility class to set the protocol and port values from the Properties instance provided,
    2525 * which can be a Properties object loaded from either build.properties or global.properties
    26  * Currently used by ServletRealmCheck and FedoraServiceProxy.
    27  * (Could also be used by Server3.java and GlobalProperties in future, if applicable.)
     26 * Currently used by Server3.java, ServletRealmCheck, FedoraServiceProxy and solr java code
     27 * GS2SolrSearch and SolrSearch.
     28 * (Could perhaps also be used by GlobalProperties in future, if applicable.)
    2829 * Can adjust fallback behaviour in this one location to make them all work consistently.
     30 *
     31 * NOTE: although global.properties contains many additional programmatically set properties
     32 * that could simplify this class, the fact that this class should work for both build.properties
     33 * and global.properties means that this class will work only with the properties common to
     34 * both property files.
    2935 */
    30 public class ProtocolPortProperties {
    31 
    32     public final boolean legacyMode; // true if before https protocol supported
     36public class ProtocolPortProperties {   
    3337
    3438    public static final int ALL_CORRECT = 0;
    3539    public static final int SECONDARY_PORT_NON_NUMERIC = 1;
    3640    public static final int INVALID_PRIMARY_PORT = 2;
    37     public static final int OLD_TOMCATPORT_BUT_NO_VALUE = 3;
    38     public static final int NO_PROTOCOL_OR_PORT = 4;
    39     public static final int INVALID_PROTOCOL = 5;
    40     public static final int HTTPS_INCONSISTENCY = 6;
    41 
    42     private static final String defProtocol = "http";
    43     private static final String defHttpPort = "8383";
    44     private static final String defHttpsPort = "8443";
    45 
    46     private String protocol; //= defProtocol;
    47     private String port; //defHttpPort; //defProtocol.equals("https") ? defHttpsPort : defHttpPort; // port that matches protocol as String
    48     private String httpPort; //defHttpPort;
    49     private String httpsPort; //defHttpsPort;
     41    public static final int NO_PROTOCOL = 3;
     42    public static final int INVALID_PROTOCOL = 4;
     43    public static final int NO_HTTP_PORT = 5;
     44    public static final int NO_HTTPS_PORT = 6;
     45
     46    private static final String FALLBACK_PROTOCOL = "http";
     47    private static final String FALLBACK_HTTP_PORT = "8383";
     48    private static final String FALLBACK_HTTPS_PORT = "8443";
     49
     50    private String protocol;
     51    private String port;
     52    private String httpPort;
     53    private String httpsPort;
    5054    //private int portNum = -1; // port that matches protocol as int
    5155
    5256    private String errorMsg;
    53     private int errorCode;
    54 
    55     public String getProtocol() { return protocol; }
     57    private int errorCode = ALL_CORRECT;
     58
     59    private boolean httpRestrictedToLocal = true;
     60    private boolean supportsHttps = false;
     61    private String defaultPortPropertyName = "localhost.port.http";
     62
     63    // default protocol if multiple supported
     64    public String getProtocol() { return protocol; }
     65    // default port (port for default protocol)
    5666    public String getPort() { return port; }
     67
     68    // httpPort is always set, but http may or may not be public
    5769    public String getHttpPort() { return httpPort; }
     70    // httpsPort is null if https not supported
    5871    public String getHttpsPort() { return httpsPort; }
    59     public int getPortNum() { return Integer.parseInt(port); }   
     72    public int getPortNum() { return Integer.parseInt(port); } 
     73
     74    // http is always available locally (over 127.0.0.1). But http may or may not be public.
     75    // Returns true if public http support requested
     76    public boolean supportsHttp() { return !httpRestrictedToLocal; }
     77    // true if https is supported
     78    public boolean supportsHttps() { return supportsHttps; }
     79
     80    // used by Server3.java to know the name of the port property to load
     81    public String getDefaultPortPropertyName() { return defaultPortPropertyName; }
    6082
    6183    public String getErrorMsg() { return errorMsg; }
     
    6486    public boolean hadError() { return errorCode != ALL_CORRECT; }
    6587
    66     // Won't attempt to recover on error.
    67     // This means on error, port etc values will be invalid
     88    // Use 127.0.0.1 instead of localhost since localhost is unsafe (can be mapped
     89    // to something other than 127.0.0.1). See https://letsencrypt.org/docs/certificates-for-localhost/
     90    public String getLocalHttpBaseAddress() {
     91    // httpPort is set during the constructor,
     92    // so knowing httpPort, we can set the internal/local access http URL:
     93    String portSuffix = httpPort.equals("80") ? "" : (":"+httpPort);
     94    return "http://127.0.0.1"+portSuffix;
     95   
     96    }
     97
     98    // Constructor that will throw an Exception on ports/protocol configuration error or inconsistency
    6899    public ProtocolPortProperties(Properties props) throws Exception {
    69100    this(props, false);
    70101    }
    71102
     103    // If param recover is true, will attempt to fallback to sane values for protocol and ports.
     104    // You can still check for error by calling hadError(), getErrorMsg() and getErrorCode().
     105    // If param recover is false, will throw an Exception on bad ports/protocol configuration
     106    // and the error message is available as the exception description.
    72107    public ProtocolPortProperties(Properties props, boolean recover) throws Exception
    73108    {
    74109    StringBuffer msg = new StringBuffer();
    75110   
    76     port = props.getProperty("tomcat.port");
    77 
    78     // We could either be dealing with properties files from before https support was introduced, in which case we want to be backwards compatible
    79     // Or we're dealing with properties files after https support was introduced.   
    80     // To determine which, can ignore server.protocol: server.protocol was introduced at a time when tomcat.port was still in effect,
    81     // server.protocol's presence does not indicate whether our GS3 installation supports https or not. Only the tomcat.port properties
    82     // indicates that: if tomcat.port exists BUT tomcat.port.http(s) don't, then it's an older GS3 that has no https support, so default to http.
    83     // If there is no tomcat.port at all, but there is a server.protocol check for the newer tomcat.port.<protocol> properties.
    84     // NOTE: global.properties still has a property called tomcat.port. We're only dealing with the old tomcat.port-only way if tomcat.port.http(s) don't exist
    85    
    86     if(props.getProperty("tomcat.port.http") == null && props.getProperty("tomcat.port.https") == null && port != null) {
    87                 // tomcat.port exists but tomcat.port.http(s) don't, so this is a GS3 before https support.
    88         legacyMode = true;
    89 
    90         // Back when tomcat.port was used AND tomcat.port.http(s) didn't exist, server.protocol if specified
    91         // would always be treated as http regardless of what it was set to
    92        
    93         protocol = defProtocol; // tomcat.port.http(s) doesn't exist, just use http
    94        
    95        
    96         if(port.equals("")) {
    97         errorCode =  OLD_TOMCATPORT_BUT_NO_VALUE;
    98         msg.append("tomcat.port enabled but did not have a value.");
    99         if(recover) {           
    100             port = httpPort = defHttpPort;
     111    httpPort = props.getProperty("localhost.port.http");
     112    if(httpPort == null || httpPort.equals("")) {
     113        errorCode = NO_HTTP_PORT;
     114        msg.append("compulsory property localhost.port.http has no value (must be set to a valid port number not already in use).");
     115        httpPort = FALLBACK_HTTP_PORT;
     116    }
     117   
     118    String supportedProtocols = props.getProperty("server.protocols");
     119    if(supportedProtocols == null || supportedProtocols.equals("")) {
     120        errorCode = NO_PROTOCOL;
     121        msg.append("server.protocols not set.");
     122        protocol = FALLBACK_PROTOCOL; // fallback to http as default (and sole) protocol
     123        port = httpPort; // set default port to httpPort
     124    }
     125    else { // supportedProtocols was assigned something
     126
     127        if(!supportedProtocols.contains("http")) {
     128        errorCode = INVALID_PROTOCOL;
     129        msg.append("server.protocols must contain http or https, or both (in order of preference) separated by commas.");
     130        protocol = FALLBACK_PROTOCOL;
     131        port = httpPort;
     132        }
     133
     134        // set available protocols with their ports
     135        if(supportedProtocols.contains("https")) {
     136        supportsHttps = true;
     137        httpsPort = props.getProperty("tomcat.port.https");
     138        if(httpsPort == null || httpsPort.equals("")) {
     139            errorCode = NO_HTTPS_PORT;
     140            msg.append("server.protocols includes https but property tomcat.port.https has no value (must be set to a valid port number not already in use).");
     141            httpsPort = FALLBACK_HTTPS_PORT;
    101142        }
    102         } else { // No issues: using tomcat.port is the pre-https way.     
    103         errorCode = ALL_CORRECT;
    104         httpPort = port;
    105         }
    106     }
    107     else {
    108         legacyMode = false;
    109 
    110         protocol = props.getProperty("server.protocol");
    111         if(protocol == null || (!protocol.equals("http") && !protocol.equals("https"))) {
    112         if(port == null) { // if tomcat.port is null AND server.protocol is null or wrong. Something very wrong with the properties file
    113             errorCode = NO_PROTOCOL_OR_PORT;
    114             msg.append("server.protocol not set. And can't determine port.");
    115         } else if(protocol == null) {           
    116             errorCode = NO_PROTOCOL_OR_PORT;
    117             msg.append("server.protocol not set.");
    118         } else {
    119             errorCode = INVALID_PROTOCOL;
    120             msg.append("server.protocol property must be http or https, but is set to invalid value. And can't determine port.");
    121         }
    122 
    123         if(recover) {
    124             protocol = defProtocol;
    125             port = httpPort = defHttpPort;
    126         }
    127 
    128         } else { // server.protocol was explicitly set and set to an acceptable value, try to find matching tomcat.port
    129        
    130         port = props.getProperty("tomcat.port."+protocol); // tomcat.port.http or tomcat.port.https, depending on server.protocol.
    131 
    132         // Handle cases where there's some inconsistency in the properties file between protocol and port.
    133         // i.e. if server.protocol=http, then tomcat.port.http must be set too.
    134         if(port == null || port.equals("")) {
    135             errorCode = INVALID_PROTOCOL;
    136             msg.append("server.protocol="+protocol+", but matching tomcat.port."+protocol+"not enabled or set.");
    137        
    138             if(recover) {
    139             protocol = defProtocol;
    140             if(protocol.startsWith("https")) { // server.protocol=https yet tomcat.port.https not provided
    141                 port = httpsPort = defHttpsPort; // use default https port to set
    142             } else {
    143                 port = httpPort = defHttpPort;
    144             }
    145             }           
    146 
    147         } else { //tomcat.port.<protocol> property matching server.protocol is set, not checking if it's an int and a valid port, though
    148             errorCode = ALL_CORRECT;
    149             // port is set
    150 
    151             if(protocol.startsWith("https")) {
    152             httpsPort = port;
    153             httpPort = props.getProperty("tomcat.port.http"); // httpPort will remain null if tomcat.port.http not set
    154             } else { // protocol=http
    155             httpPort = port;
    156             httpsPort = props.getProperty("tomcat.port.https"); // httpsPort will remain null if tomcat.port.https not set
    157             }
    158         }
    159         }
    160     }   
    161    
    162     if(errorCode == ALL_CORRECT) { // then check any assigned ports are valid
     143        }
     144        if(supportedProtocols.matches("http(,|\\s+|$)")) {
     145        httpRestrictedToLocal = false;
     146        }
     147   
     148        // set default protocol and default port: the first protocol in the supportedProtocols list
     149        if(supportedProtocols.matches("^[,\\s]*https")) {
     150        protocol = "https";
     151        port = httpsPort;       
     152        } else {
     153        protocol = "http";
     154        port = httpPort;
     155        }
     156    }
     157
     158    if(!recover && errorCode == ALL_CORRECT) { // then check any assigned ports are valid
    163159        if(httpPort != null) {
    164160        try {
    165161            Integer.parseInt(httpPort);
    166162        } catch(NumberFormatException nfe) {
    167             msg.append("\nInvalid port specified for over http: not numeric.");
     163            msg.append("\nInvalid localhost.port.http specified: must be numeric.");
    168164            if(port == httpPort) {
    169             errorCode = INVALID_PRIMARY_PORT;
    170             if(recover) {
    171                 port = httpPort = defHttpPort;
    172             } else { // no recovery requested, so if port for protocol is non-numeric, consider it a 'fatal' error
    173                 port = httpPort = httpsPort = null;
    174             }
    175             } else { // secondary protocol's port is non-numeric, not fatal?
     165            errorCode = INVALID_PRIMARY_PORT;           
     166            port = httpPort = FALLBACK_HTTP_PORT; // recovery values that can work with default protocol           
     167            } else { // secondary protocol's port is non-numeric, not fatal in recovery mode
    176168            errorCode = SECONDARY_PORT_NON_NUMERIC;
    177             if(recover) {
    178                 msg.append("\nNot using this port");
    179             }
    180             httpPort = null;           
     169            httpPort = null;
     170            if(recover) msg.append("\nNot using this port");
    181171            }
    182172        }
     
    187177            Integer.parseInt(httpsPort);
    188178        } catch(NumberFormatException nfe) {
    189             msg.append("\nInvalid port specified for over https: not numeric.");
     179            msg.append("\nInvalid port specified for over https (tomcat.port.https): must be numeric.");
    190180            if(port == httpsPort) {
    191             errorCode = INVALID_PRIMARY_PORT;
    192             if(recover) {
    193                 port = httpsPort = defHttpsPort;
    194             } else { // primary port affected and not asked to recover, treat as fatal
    195                 port = httpsPort = httpPort = null;
    196             }
    197             } else { // non primary port is invalid/non-numeric, not fatal
     181            errorCode = INVALID_PRIMARY_PORT;           
     182            port = httpsPort = FALLBACK_HTTPS_PORT; // recovery values that work with default protocol 
     183            } else { // non primary port is invalid/non-numeric, not fatal in recovery mode
    198184            errorCode = SECONDARY_PORT_NON_NUMERIC;
    199             if(recover) {
    200                 msg.append("\nNot using this port");
    201             }
    202185            httpsPort = null;
     186            if(recover) msg.append("\nNot using this port");
    203187            }
    204188        }
     
    206190    }
    207191   
     192    // if the default port is the httpsPort, then modify the defaultPortPropertyName to match
     193    // (else it will remain at the property name for the http port)
     194    if(port == httpsPort) {
     195        defaultPortPropertyName = "tomcat.port.https";
     196    }
    208197
    209198    if(recover) {
    210199        msg.append("\nFalling back to port ").append(port).append(" and protocol ").append(protocol).append(".");
    211200    }
    212     // else if(errorCode == ALL_CORRECT) {
    213     //    msg.append("\nUsing port ").append(port).append(" and protocol ").append(protocol).append(".");
    214     //} // else invalid property value(s) and we've not been asked to recover. msg alreay set.
    215201   
    216202    errorMsg = msg.toString();
Note: See TracChangeset for help on using the changeset viewer.