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  from application import ValidationError 
 28   
29 -def validate_param(key, vals, minreps, maxreps, pattern, 30 compiled_pattern, default, doc):
31 """Validate a particular parameter. 32 33 """ 34 l = len(vals) 35 36 # If not present, and there is a default, set vals to default 37 if l == 0 and default is not None: 38 vals = default 39 40 # Check we've got an acceptable number of values. 41 if l < minreps: 42 raise ValidationError(u"Too few instances of %r supplied " 43 u"(needed %d, got %d)" % 44 (key, minreps, l)) 45 if maxreps is not None and l > maxreps: 46 raise ValidationError(u"Too many instances of %r supplied " 47 u"(maximum %d, got %d)" % 48 (key, maxreps, l)) 49 50 if compiled_pattern is not None: 51 # Check the regexp pattern matches 52 for val in vals: 53 if not compiled_pattern.match(val): 54 raise ValidationError(u"Invalid parameter value for %r" % key) 55 56 return vals
57
58 -def validate_params(requestobj, constraints):
59 """Validate parameters, raising ValidationError for problems. 60 61 `constraints` is a dict of tuples, one for each field. Unknown fields 62 raise an error. 63 64 """ 65 p = {} 66 67 # Check for missing parameters - add if they have a default, otherwise give 68 # an error. 69 missing_params = set() 70 for key, constraint in constraints.iteritems(): 71 if constraint[4] is not None: 72 if key not in requestobj: 73 p[key] = constraint[4] 74 else: 75 # check for missing params 76 if constraint[0] > 0 and key not in requestobj: 77 missing_params.add(key) 78 79 if len(missing_params) != 0: 80 # We trust the list of missing_params not to be trying to hack us. 81 raise ValidationError(u"Missing required parameters %s" % 82 u', '.join(u"'%s'" % p for p in missing_params)) 83 84 for key in requestobj: 85 constraint = constraints.get(key, None) 86 if constraint is None: 87 if re.match('\w+$', key): 88 # No potentially dangerous characters 89 raise ValidationError(u"Unknown parameter %r supplied" % key) 90 else: 91 raise ValidationError(u"Unknown parameter supplied") 92 p[key] = validate_param(key, requestobj[key], *constraint) 93 94 return p
95
96 -def check_valid_params(request, props):
97 constraints = props.get('valid_params', None) 98 if constraints != None: 99 request.params = validate_params(request.params, constraints) 100 return request
101
102 -def check_no_params(request, props):
103 if len(request.params) != 0: 104 raise ValidationError(u"This resource does not accept parameters") 105 return request
106 107
108 -def validate_pathinfo_params(request, param_rules):
109 """Check that the pathinfo satisfies the supplied rules. 110 111 """ 112 index = 0 113 for name, pattern, compiled_pattern, default, required in param_rules: 114 if len(request.pathinfo.tail) <= index: 115 if required: 116 raise ValidationError("Required pathinfo component missing") 117 # Put default value into dictionary. 118 request.pathinfo[name] = default 119 index += 1 120 continue 121 param = request.pathinfo.tail[index] 122 index += 1 123 # Validate the param, and put it into the dictionary. 124 if compiled_pattern is not None: 125 if not compiled_pattern.match(param): 126 raise ValidationError("Pathinfo component does not match " 127 "allowed pattern") 128 request.pathinfo[name] = param 129 request.pathinfo.tail = request.pathinfo.tail[index:]
130
131 -def validate_pathinfo_tail(request, tail_rules):
132 """Check that the pathinfo tail satisfies the supplied rules. 133 134 """ 135 if tail_rules is None: 136 if len(request.pathinfo.tail) > 0: 137 raise ValidationError("Unexpected trailing pathinfo") 138 else: 139 return 140 minreps, maxreps, pattern, compiled_pattern, default, doc = tail_rules
141 # FIXME - validate pathinfo 142
143 -def check_pathinfo(request, props):
144 """Check the pathinfo for validity, and populate the pathinfo dictionary. 145 146 """ 147 param_rules = props['pathinfo_params_rules'] 148 tail_rules = props['pathinfo_tail_rules'] 149 150 validate_pathinfo_params(request, param_rules) 151 validate_pathinfo_tail(request, tail_rules) 152 153 return request
154
155 -def _pad_none(args, length):
156 """Pad a list of arguments with None to specified length. 157 158 """ 159 return (list(args) + [None] * length)[:length]
160
161 -def parse_pathinfo_rules(pathinfo_items, tail_rules):
162 """Parse pathinfo rules. 163 164 """ 165 # Build the parameter validation rules from the args. 166 param_rules = [] 167 previous_required = True 168 for pathinfo_item in pathinfo_items: 169 if len(pathinfo_item) < 1 or len(pathinfo_item) > 3: 170 raise TypeError("pathinfo decorator arguments must be " 171 "sequences of length 1 to 3 items - got %d" 172 " items" % len(pathinfo_item)) 173 required = False 174 if len(pathinfo_item) < 3: 175 # No default specified, so a required parameter. 176 required = True 177 if not previous_required: 178 raise TypeError("required parameter in pathinfo decorator" 179 " following non-required parameter") 180 else: 181 previous_required = False 182 name, pattern, default = _pad_none(pathinfo_item, 3) 183 compiled_pattern = None 184 if pattern is not None: 185 compiled_pattern = re.compile(pattern, re.UNICODE) 186 param_rules.append((name, pattern, compiled_pattern, default, required)) 187 188 # Check the "tail" keyword argument. 189 if not tail_rules is None: 190 if len(tail_rules) > 5: 191 raise TypeError("pathinfo tail argument must be " 192 "sequence of length 0 to 5 items - got %d " 193 "items" % len(tail_rules)) 194 tail_rules = _pad_none(tail_rules, 5) 195 pattern = tail_rules[2] 196 compiled_pattern = None 197 if pattern is not None: 198 compiled_pattern = re.compile(pattern, re.UNICODE) 199 tail_rules = tail_rules[:2] + [compiled_pattern] + tail_rules[2:] 200 201 return param_rules, tail_rules
202 203 # vim: set fileencoding=utf-8 : 204