001    package hirondelle.web4j.request;
002    
003    import hirondelle.web4j.action.Action;
004    import hirondelle.web4j.model.ModelCtorException;
005    import hirondelle.web4j.model.ModelFromRequest;
006    import hirondelle.web4j.model.ModelUtil;
007    import hirondelle.web4j.readconfig.Config;
008    import hirondelle.web4j.security.ApplicationFirewall;
009    import hirondelle.web4j.util.Util;
010    
011    import java.util.regex.Pattern;
012    
013    /**
014     <span class="highlight">Request parameter as a name and 
015     (usually) an associated <em>regular expression</em>.</span>
016    
017     <P>This class does not <em>directly</em> provide access to the parameter <em>value</em>.
018     For such services, please see {@link RequestParser} and {@link ModelFromRequest}. 
019      
020     <P>This class separates request parameters into two kinds : file upload request 
021     parameters, and all others (here called "regular" request parameters).
022      
023     <P><b><a name="Regular">Regular Request Parameters</a></b><br>
024     Regular request parameters are associated with :
025     <ul>
026     <li>a name, corresponding to both an underlying HTTP request parameter name, and 
027     an underlying control name - see <a href="#NamingConvention">naming convention</a>. 
028     <li>a regular expression, used by {@link ApplicationFirewall} to perform 
029     <a href="ApplicationFirewall.html#HardValidation">hard validation</a>  
030     </ul> 
031     
032     <P><b><a name="FileUpload">File Upload Request Parameters</a></b><br>
033     Files are uploaded using forms having : 
034    <ul>
035     <li> <tt>method="POST"</tt>
036     <li> <tt>enctype="multipart/form-data"</tt>
037     <li> an <tt>&lt;INPUT type="file"&gt;</tt> control
038    </ul>
039     
040     <P>In addition, note that the Servlet API does <em>not</em> have extensive services for 
041     processing file upload parameters. It is likely best to use a third party tool for 
042     that task. 
043     
044     <P>File upload request parameters, <em>as represented by this class</em>, have only a
045     name associated with them, and no regular expression. This is because WEB4J 
046     cannot perform <a href="ApplicationFirewall.html#HardValidation">hard validation</a>  
047     on the value of a file upload parameter - since the user may select any file whatsoever, 
048     validation of file contents can only be treated 
049     as a <a href="ApplicationFirewall.html#SoftValidation">soft validation</a>. If there is a 
050     problem, the response to the user must be polished, as part of the normal operation of 
051     the application.  
052     
053     <P>As an example, an {@link Action} might perform 
054     <a href="ApplicationFirewall.html#SoftValidation">soft validation</a> on a file upload parameter 
055     for these items : 
056     <ul>   
057     <li>file size does not exceed a maximum value
058     <li>MIME type matches a regular expression  
059     <li>file name matches a regular expression
060     <li>text file content may be matched to a regular expression
061     </ul> 
062     
063     <P><b><a name="NamingConvention">Naming Convention</a></b><br>
064     <span class="highlight">Parameter names are usually not arbitrary in WEB4J.</span>
065     Instead, a simple convention is used which allows for automated mapping between 
066     request parameter names and corresponding <tt>getXXX</tt> methods of Model Objects  
067     (see {@link hirondelle.web4j.ui.tag.Populate}). For example, a parameter 
068     named <tt>'Birth Date'</tt> (or <tt>'birthDate'</tt>) is mapped to a method named 
069     <tt>getBirthDate()</tt> when prepopulating a form with the contents of 
070     a Model Object. (The <tt>'Birth Date'</tt> naming style is recommended, since it 
071     has this advantage : when messages regarding form input are presented to the user, 
072     the control name may be used directly, without trivial mapping  
073     of a 'coder-friendly' parameter name into more user-friendly text.)
074      
075     <P> Some parameters - notably those passed to <tt>Template.jsp</tt> - are not
076     processed at all by the <tt>Controller</tt>, but are used directly in JSPs 
077     instead. Such parameters do not undergo 
078     <a href="ApplicationFirewall.html#HardValidation">hard validation</a> by the 
079     {@link hirondelle.web4j.security.ApplicationFirewall}, and are not represented by this class.
080     
081     <P> See {@link java.util.regex.Pattern} for more information on regular expressions.
082    */
083    public final class RequestParameter { 
084      
085      /**
086       Return a <a href="#Regular">regular parameter</a> hard-validated only for 
087       name and size. 
088       
089       <P>The size is taken from the <tt>MaxRequestParamValueSize</tt> setting in <tt>web.xml</tt>.
090        
091       @param aName name of the underlying HTTP request parameter. See 
092       <a href="#NamingConvention">naming convention</a>. 
093      */
094      public static RequestParameter withLengthCheck(String aName){
095        Config config = new Config();
096        String regex = "(.){0," + config.getMaxRequestParamValueSize() + "}";
097        Pattern lengthPattern = Pattern.compile(regex, Pattern.DOTALL);
098        return withRegexCheck(aName, lengthPattern);
099      }
100      
101      /**
102       Return a <a href="#Regular">regular parameter</a> hard-validated for name and 
103       for value matching a regular expression.
104       
105       @param aName name of the underlying HTTP request parameter. See 
106       <a href="#NamingConvention">naming convention</a>. 
107       @param aValueRegex regular expression for doing hard validation of the request 
108       parameter value(s).
109      */
110      public static RequestParameter withRegexCheck(String aName, Pattern aValueRegex){
111        return new RequestParameter(aName, aValueRegex);    
112      }
113      
114      /**
115       Return a <a href="#Regular">regular parameter</a> hard-validated for name and 
116       for value matching a regular expression.
117       
118       @param aName name of the underlying HTTP request parameter. See 
119       <a href="#NamingConvention">naming convention</a>. 
120       @param aValueRegex regular expression for doing hard validation of the request 
121       parameter value(s).
122      */
123      public static RequestParameter withRegexCheck(String aName, String aValueRegex){
124        return new RequestParameter(aName, aValueRegex);    
125      }
126      
127      /**
128       Constructor for a <a href="#FileUpload">file upload</a> request parameter. 
129       
130       @param aName name of the underlying HTTP request parameter. See 
131       <a href="#NamingConvention">naming convention</a>. 
132      */
133      public static RequestParameter forFileUpload(String aName){
134        return new RequestParameter(aName); 
135      }
136      
137      /** Return the request parameter name.  */
138      public String getName(){
139        return fName;
140      }
141       
142      /**
143       Return the regular expression associated with this <tt>RequestParameter</tt>. 
144       
145       <P>This regular expression is used to perform 
146       <a href="ApplicationFirewall.html#HardValidation">hard validation</a> of this parameter's value(s).
147       
148       <P>This method will return <tt>null</tt> only for <a href="#FileUpload">file upload</a> parameters.
149      */
150      public Pattern getRegex(){
151        return fRegex;
152      }
153      
154      /**
155       Return <tt>true</tt> only if {@link #forFileUpload} was used to build this object. 
156      */
157      public boolean isFileUploadParameter() {
158        return ! fIsRegularParameter;
159      }
160      
161      /**
162       Return <tt>true</tt> only if <tt>aRawParamValue</tt> satisfies the regular expression 
163       {@link #getRegex()}, <em>or</em> if this is a <a href="#FileUpload">file upload</a> 
164       request parameter.
165       
166       <P>Always represents a <a href="ApplicationFirewall.html#HardValidation">hard validation</a>, not a 
167       soft validation.
168      */
169      public boolean isValidParamValue(String aRawParamValue){
170        boolean result = false;
171        if ( isFileUploadParameter() ){
172          result = true;   
173        }
174        else {
175          result = Util.matches(fRegex, aRawParamValue);
176        }
177        return result;
178      }
179      
180      @Override public boolean equals(Object aThat){
181        if (this == aThat) return true;
182        if ( !(aThat instanceof RequestParameter) ) return false;
183        RequestParameter that = (RequestParameter)aThat;
184        return ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
185      }
186      
187      @Override public int hashCode(){
188        return ModelUtil.hashCodeFor(getSignificantFields());
189      }
190    
191      /** Intended for debugging only.  */
192      @Override public String toString() {
193        String result = null;
194        if ( isFileUploadParameter() ) {
195          result = "Name[File Upload]: " + fName;  
196        }
197        else {
198          result = "Name:" + fName + " Regex:" + fRegex;  
199        }
200        return result;
201      } 
202      
203      // PRIVATE  
204      private final String fName;
205      private Pattern fRegex; //Patterns are immutable
206      private final boolean fIsRegularParameter;
207      
208      /**
209       Constructor for a <a href="#Regular">"regular"</a> request parameter. 
210       
211       @param aName name of the underlying HTTP request parameter. See 
212       <a href="#NamingConvention">naming convention</a>. 
213       @param aRegex regular expression for doing hard validation of the request 
214       parameter value(s).
215      */
216      private RequestParameter(String aName, String aRegex) { 
217        this(aName, Pattern.compile(aRegex));
218        validateState();
219      }
220       
221      /**
222       Constructor for a <a href="#Regular">"regular"</a> request parameter.
223        
224       @param aName name of the underlying HTTP request parameter. See 
225       <a href="#NamingConvention">naming convention</a>. 
226       @param aRegex regular expression for doing hard validation of the request 
227       parameter value(s).
228      */
229      private RequestParameter(String aName, Pattern aRegex) {
230        fName = aName;
231        fRegex = aRegex;
232        fIsRegularParameter = true;
233        validateState();
234      }
235      
236      /**
237       Constructor for a <a href="#FileUpload">file upload</a> request parameter.
238        
239       @param aName name of the underlying HTTP request parameter. See 
240       <a href="#NamingConvention">naming convention</a>. 
241      */
242      private RequestParameter(String aName) {
243        fName = aName;
244        fIsRegularParameter = false;
245        validateState();
246      }
247     
248      private void validateState(){
249        //use the model ctor exception only to gather errors together
250        //it is never actually thrown.
251        ModelCtorException ex = new ModelCtorException();
252        if ( ! Util.textHasContent(fName) ){
253          ex.add("Name must have content.");
254        }
255        if ( fIsRegularParameter && (fRegex == null || ! Util.textHasContent(fRegex.pattern()) ) ){
256          ex.add("For regular request parameters, regex pattern must be present.");
257        }
258        if ( ! fIsRegularParameter && fRegex != null  ){
259          ex.add("For file upload parameters, regex pattern must be null.");
260        }
261        if ( ex.isNotEmpty() ) {
262          throw new IllegalArgumentException(ex.getMessages().toString());
263        }
264      }
265      
266      private Object[] getSignificantFields(){
267        return new Object[] {fName, fRegex};
268      }
269    }