Package wsgiwapi :: Module validation
[frames] | no frames]

Source Code for Module wsgiwapi.validation

  1  # Copyright (c) 2009 Richard Boulton 
  2  # 
  3  # Permission is hereby granted, free of charge, to any person obtaining a copy 
  4  # of this software and associated documentation files (the "Software"), to deal 
  5  # in the Software without restriction, including without limitation the rights 
  6  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
  7  # copies of the Software, and to permit persons to whom the Software is 
  8  # furnished to do so, subject to the following conditions: 
  9  # 
 10  # The above copyright notice and this permission notice shall be included in 
 11  # all copies or substantial portions of the Software. 
 12  # 
 13  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 14  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 15  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 16  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 17  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 18  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 19  # SOFTWARE. 
 20  r"""Support for validation of request parameters. 
 21   
 22  """ 
 23  __docformat__ = "restructuredtext en" 
 24   
 25  import re 
 26  from wsgisupport import HTTPNotFound 
 27   
28 -class ValidationError(Exception):
29 """Exception used to indicate that parameters failed validation. 30 31 """
32 - def __init__(self, message):
33 self.message = message
34
35 - def __str__(self):
36 return "ValidationError(\"%s\")" % self.message.\ 37 replace('\\', '\\\\').\ 38 replace('"', '\"')
39
40 -def validate_param(key, vals, minreps, maxreps, pattern, 41 compiled_pattern, default, doc):
42 """Validate a particular parameter. 43 44 """ 45 l = len(vals) 46 47 # If not present, and there is a default, set vals to default 48 if l == 0 and default is not None: 49 vals = default 50 51 # Check we've got an acceptable number of values. 52 if l < minreps: 53 raise ValidationError(u"Too few instances of %r supplied " 54 u"(needed %d, got %d)" % 55 (key, minreps, l)) 56 if maxreps is not None and l > maxreps: 57 raise ValidationError(u"Too many instances of %r supplied " 58 u"(maximum %d, got %d)" % 59 (key, maxreps, l)) 60 61 if compiled_pattern is not None: 62 # Check the regexp pattern matches 63 for val in vals: 64 if not compiled_pattern.match(val): 65 raise ValidationError(u"Invalid parameter value for %r" % key) 66 67 return vals
68
69 -def validate_params(requestobj, constraints):
70 """Validate parameters, raising ValidationError for problems. 71 72 `constraints` is a dict of tuples, one for each field. Unknown fields 73 raise an error. 74 75 """ 76 p = {} 77 78 # Check for missing parameters - add if they have a default, otherwise give 79 # an error. 80 missing_params = set() 81 for key, constraint in constraints.iteritems(): 82 if constraint[4] is not None: 83 if key not in requestobj: 84 p[key] = constraint[4] 85 else: 86 # check for missing params 87 if constraint[0] > 0 and key not in requestobj: 88 missing_params.add(key) 89 90 if len(missing_params) != 0: 91 # We trust the list of missing_params not to be trying to hack us. 92 raise ValidationError(u"Missing required parameters %s" % 93 u', '.join(u"'%s'" % p for p in missing_params)) 94 95 for key in requestobj: 96 constraint = constraints.get(key, None) 97 if constraint is None: 98 if re.match('\w+$', key): 99 # No potentially dangerous characters 100 raise ValidationError(u"Unknown parameter %r supplied" % key) 101 else: 102 raise ValidationError(u"Unknown parameter supplied") 103 p[key] = validate_param(key, requestobj[key], *constraint) 104 105 return p
106
107 -def check_valid_params(request, props):
108 constraints = props.get('valid_params', None) 109 if constraints != None: 110 request.params = validate_params(request.params, constraints) 111 return request
112
113 -def check_no_params(request, props):
114 if len(request.params) != 0: 115 raise ValidationError(u"This resource does not accept parameters") 116 return request
117 118
119 -def validate_pathinfo_params(request, param_rules):
120 """Check that the pathinfo satisfies the supplied rules. 121 122 """ 123 index = 0 124 for name, pattern, compiled_pattern, default, required in param_rules: 125 if len(request.pathinfo.tail) <= index: 126 if required: 127 raise HTTPNotFound(request.path) 128 # Put default value into dictionary. 129 request.pathinfo[name] = default 130 index += 1 131 continue 132 param = request.pathinfo.tail[index] 133 index += 1 134 # Validate the param, and put it into the dictionary. 135 if compiled_pattern is not None: 136 if not compiled_pattern.match(param): 137 raise HTTPNotFound(request.path) 138 request.pathinfo[name] = param 139 request.pathinfo.tail = request.pathinfo.tail[index:]
140
141 -def validate_pathinfo_tail(request, tail_rules):
142 """Check that the pathinfo tail satisfies the supplied rules. 143 144 """ 145 if tail_rules is None: 146 if len(request.pathinfo.tail) > 0: 147 raise HTTPNotFound(request.path) 148 else: 149 return 150 minreps, maxreps, pattern, compiled_pattern, default, doc = tail_rules
151 # FIXME - validate pathinfo 152
153 -def check_pathinfo(request, props):
154 """Check the pathinfo for validity, and populate the pathinfo dictionary. 155 156 """ 157 param_rules = props['pathinfo_params_rules'] 158 tail_rules = props['pathinfo_tail_rules'] 159 160 validate_pathinfo_params(request, param_rules) 161 validate_pathinfo_tail(request, tail_rules) 162 163 return request
164
165 -def _pad_none(args, length):
166 """Pad a list of arguments with None to specified length. 167 168 """ 169 return (list(args) + [None] * length)[:length]
170
171 -def parse_pathinfo_rules(pathinfo_items, tail_rules):
172 """Parse pathinfo rules. 173 174 """ 175 # Build the parameter validation rules from the args. 176 param_rules = [] 177 previous_required = True 178 for pathinfo_item in pathinfo_items: 179 if len(pathinfo_item) < 1 or len(pathinfo_item) > 3: 180 raise TypeError("pathinfo decorator arguments must be " 181 "sequences of length 1 to 3 items - got %d" 182 " items" % len(pathinfo_item)) 183 required = False 184 if len(pathinfo_item) < 3: 185 # No default specified, so a required parameter. 186 required = True 187 if not previous_required: 188 raise TypeError("required parameter in pathinfo decorator" 189 " following non-required parameter") 190 previous_required = True 191 name, pattern, default = _pad_none(pathinfo_item, 3) 192 compiled_pattern = None 193 if pattern is not None: 194 compiled_pattern = re.compile(pattern) 195 param_rules.append((name, pattern, compiled_pattern, default, required)) 196 197 # Check the "tail" keyword argument. 198 if not tail_rules is None: 199 if len(tail_rules) > 5: 200 raise TypeError("pathinfo tail argument must be " 201 "sequence of length 0 to 5 items - got %d " 202 "items" % len(tail_rules)) 203 tail_rules = _pad_none(tail_rules, 5) 204 pattern = tail_rules[2] 205 compiled_pattern = None 206 if pattern is not None: 207 compiled_pattern = re.compile(pattern) 208 tail_rules = tail_rules[:2] + [compiled_pattern] + tail_rules[2:] 209 210 return param_rules, tail_rules
211 212 # vim: set fileencoding=utf-8 : 213