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><INPUT type="file"></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 }